2014-03-13
The argument about unbound methods versus functions
As mentioned in How functions become bound or unbound methods, in Python 2 when you access a function on
a class (eg you do cls.func
) it becomes an 'unbound method'. In
Python 3 this is gone, as Peter Donis
mentioned in a comment on that entry; if you do cls.func
you get
back the plain function. I'm not entirely sure how I feel about
this, so let's start by asking a relevant question: what's the
difference between an unbound method and the underlying function?
The answer is that calling an unbound method adds extra type checking
on the first argument. When cls.func
is an unbound method and you
call it, the first argument must be an instance of cls
or a
subclass of it (ie, something for which isinstance()
would return
True
). If it isn't you get a TypeError
(much like the ones we
saw back here). Calling the
function directly has no such requirement; you can feed it anything
at all, even though as a class method it's probably expecting an
instance of the class as its first argument.
I'll admit that it's an open argument whether this type checking
is a good thing or not. It's certainly atypical for Python and the
conversion from a plain function into an unbound method is a bit
surprising to people. There aren't that many situations in Python
where making something an attribute of a simple class magically
changes it into something else; normally you expect 'A.attr =
something; A.attr
' to give you 'something
' again. The argument
in defense of the checking is that it's a useful safety measure for
functions that are almost certainly coded with certain assumptions
and directly calling class methods on the class is not exactly a
common thing.
Now that I've written this entry, I can see why Python 3 took unbound methods out. They might be handy but they're not actually essential to how things work (unlike bound methods) and Python's mechanics are mostly about what actively has to be there. I guess my view is now that I don't mind them in Python 2 but I doubt I'm going to miss them in Python 3 (if I ever do anything with Python 3).