In Python, when should I use a function instead of a method?
My general rule is this - is the operation performed on the object or by the object?
if it is done by the object, it should be a member operation. If it could apply to other things too, or is done by something else to the object then it should be a function (or perhaps a member of something else).
When introducing programming, it is traditional (albeit implementation incorrect) to describe objects in terms of real-world objects such as cars. You mention a duck, so let's go with that.
class duck:
def __init__(self):pass
def eat(self, o): pass
def crap(self) : pass
def die(self)
....
In the context of the "objects are real things" analogy, it is "correct" to add a class method for anything which the object can do. So say I want to kill off a duck, do I add a .kill() to the duck? No... as far as I know animals do not commit suicide. Therefore if I want to kill a duck I should do this:
def kill(o):
if isinstance(o, duck):
o.die()
elif isinstance(o, dog):
print "WHY????"
o.die()
elif isinstance(o, nyancat):
raise Exception("NYAN "*9001)
else:
print "can't kill it."
Moving away from this analogy, why do we use methods and classes? Because we want to contain data and hopefully structure our code in a manner such that it will be reusable and extensible in the future. This brings us to the notion of encapsulation which is so dear to OO design.
The encapsulation principal is really what this comes down to: as a designer you should hide everything about the implementation and class internals which it is not absolutely necessarily for any user or other developer to access. Because we deal with instances of classes, this reduces to "what operations are crucial on this instance". If an operation is not instance specific, then it should not be a member function.
TL;DR: what @Bryan said. If it operates on an instance and needs to access data which is internal to the class instance, it should be a member function.
Use a class when you want to:
1) Isolate calling code from implementation details -- taking advantage of abstraction and encapsulation.
2) When you want to be substitutable for other objects -- taking advantage of polymorphism.
3) When you want to reuse code for similar objects -- taking advantage of inheritance.
Use a function for calls that make sense across many different object types -- for example, the builtin len and repr functions apply to many kinds of objects.
That being said, the choice sometimes comes down to a matter of taste. Think in terms of what is most convenient and readable for typical calls. For example, which would be better (x.sin()**2 + y.cos()**2).sqrt()
or sqrt(sin(x)**2 + cos(y)**2)
?