The Zen of Python states that there should only be one way to do things- yet frequently I run into the problem of deciding when to use a function versus when to use a method.
Let's take a trivial example- a ChessBoard object. Let's say we need some way to get all the legal King moves available on the board. Do we write ChessBoard.get_king_moves() or get_king_moves(chess_board)?
Here are some related questions I looked at:
The answers I got were largely inconclusive:
Why does Python use methods for some functionality (e.g. list.index()) but functions for other (e.g. len(list))?
The major reason is history. Functions were used for those operations that were generic for a group of types and which were intended to work even for objects that didn’t have methods at all (e.g. tuples). It is also convenient to have a function that can readily be applied to an amorphous collection of objects when you use the functional features of Python (map(), apply() et al).
In fact, implementing len(), max(), min() as a built-in function is actually less code than implementing them as methods for each type. One can quibble about individual cases but it’s a part of Python, and it’s too late to make such fundamental changes now. The functions have to remain to avoid massive code breakage.
While interesting, the above doesn't really say much as to what strategy to adopt.
This is one of the reasons - with custom methods, developers would be free to choose a different method name, like getLength(), length(), getlength() or whatsoever. Python enforces strict naming so that the common function len() can be used.
Slightly more interesting. My take is that functions are in a sense, the Pythonic version of interfaces.
Lastly, from Guido himself:
Talking about the Abilities/Interfaces made me think about some of our "rogue" special method names. In the Language Reference, it says, "A class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names." But there are all these methods with special names like
__len__
or__unicode__
which seem to be provided for the benefit of built-in functions, rather than for support of syntax. Presumably in an interface-based Python, these methods would turn into regularly-named methods on an ABC, so that__len__
would becomeclass container: ... def len(self): raise NotImplemented
Though, thinking about it some more, I don't see why all syntactic operations wouldn't just invoke the appropriate normally-named method on a specific ABC. "
<
", for instance, would presumably invoke "object.lessthan
" (or perhaps "comparable.lessthan
"). So another benefit would be the ability to wean Python away from this mangled-name oddness, which seems to me an HCI improvement.Hm. I'm not sure I agree (figure that :-).
There are two bits of "Python rationale" that I'd like to explain first.
First of all, I chose len(x) over x.len() for HCI reasons (
def __len__()
came much later). There are two intertwined reasons actually, both HCI:(a) For some operations, prefix notation just reads better than postfix -- prefix (and infix!) operations have a long tradition in mathematics which likes notations where the visuals help the mathematician thinking about a problem. Compare the easy with which we rewrite a formula like
x*(a+b)
intox*a + x*b
to the clumsiness of doing the same thing using a raw OO notation.(b) When I read code that says
len(x)
I know that it is asking for the length of something. This tells me two things: the result is an integer, and the argument is some kind of container. To the contrary, when I readx.len()
, I have to already know thatx
is some kind of container implementing an interface or inheriting from a class that has a standardlen()
. Witness the confusion we occasionally have when a class that is not implementing a mapping has aget()
orkeys()
method, or something that isn't a file has awrite()
method.Saying the same thing in another way, I see 'len' as a built-in operation. I'd hate to lose that. I can't say for sure whether you meant that or not, but 'def len(self): ...' certainly sounds like you want to demote it to an ordinary method. I'm strongly -1 on that.
The second bit of Python rationale I promised to explain is the reason why I chose special methods to look
__special__
and not merelyspecial
. I was anticipating lots of operations that classes might want to override, some standard (e.g.__add__
or__getitem__
), some not so standard (e.g. pickle's__reduce__
for a long time had no support in C code at all). I didn't want these special operations to use ordinary method names, because then pre-existing classes, or classes written by users without an encyclopedic memory for all the special methods, would be liable to accidentally define operations they didn't mean to implement, with possibly disastrous consequences. Ivan Krstić explained this more concise in his message, which arrived after I'd written all this up.-- --Guido van Rossum (home page: http://www.python.org/~guido/)
My understanding of this is that in certain cases, prefix notation just makes more sense (ie, Duck.quack makes more sense than quack(Duck) from a linguistic standpoint.) and again, the functions allow for "interfaces".
In such a case, my guess would be to implement get_king_moves based solely on Guido's first point. But that still leaves a lot of open questions regarding say, implementing a stack and queue class with similar push and pop methods- should they be functions or methods? (here I would guess functions, because I really want to signal a push-pop interface)
TLDR: Can someone explain what the strategy for deciding when to use functions vs. methods should be?