[Tutor] Unbound Method Error
Steven D'Aprano
steve at pearwood.info
Mon Apr 25 10:22:46 CEST 2011
Greg Nielsen wrote:
> In Python 2.6.6, this section of code returns the following error
>
> TypeError: unbound method collide() must be called with BlueCarSprite
> instance as first argument (got GreenCarSprite instance instead)
Please copy and paste the actual traceback, as that will show the
*actual* line of code (and not just "this section").
> 1) What exactly is an "unbound method" error? I have been programming (in
> other languages) for several years on and off and have never heard of
> something like this.
When you retrieve an attribute from an instance:
instance.name
Python looks up "name" on the instance, then class, then any parent
classes, etc. The same mechanism is used for methods: the method object
just happens to be callable.
When you retrieve instance.method, you get back a method-wrapper object
which wraps the function you defined in the class. The method-wrapper
ensures that the special "self" argument is supplied, so that this syntax:
instance.method(x, y, z)
calls the actual function object like this:
type(instance).method(instance, x, y, z)
Some people find it hard to wrap their head around this, but remember
that you have defined an ordinary function inside the class, using the
same def key word that gets used for making other functions. You write a
function, and the class wraps it in code to make it a method.
Now, when you call instance.method, the object you get back is a "bound
method", so-called because it is bound to an instance and can supply the
self argument. But when you call the method on the class object instead:
Class.method
you get an *unbound* method, which is still a wrapper around the actual
function you defined. Remember the parameter list you defined in the
function? It starts with self. If you call it via the instance, you get
a bound method and self if automatically provided. But if you call it
from the class, there is no instance supplied and you have to supply it
by hand.
You can easily experiment with this using built-ins such as str:
>>> "spam".upper
<built-in method upper of str object at 0xb7f2d780>
>>> "spam".upper() # self is set to "spam"
'SPAM'
>>> str.upper
<method 'upper' of 'str' objects>
>>> str.upper()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor 'upper' of 'str' object needs an argument
>>> str.upper("spam")
'SPAM'
Some of the implementation details (such as error messages) may differ
between methods in Python classes and methods in built-in types, but the
broad details are the same.
> 2) What changes between 3.1.3 and 2.6.6 that makes this section of code not
> work? My best guess is that I'm missing something that lets the computer
> know that BlueCarSprite is an object type, but even if that's true, I
> wouldn't know what to do to fix it.
I suspect that you're running into a small semantic difference between
Python 2 and 3. In Python 2, method objects know what their type is, and
they enforce that the self argument has the same type. In Python 3,
unbound methods are gone, and instance you get an ordinary function.
This ordinary function has no clue about what class it is attached to,
and so it can't enforce that the self parameter has the right class. You
can pass anything that works.
> 3) What would be a legit fix to my code to make it run? Sure, I guess I
> could write a Collide class and use that rather than my collide method, or
> perhaps a class that could act as a middle man between bits of code that I
> know work, but I know that there has to be a better way to fix this.
Perhaps you can make GreenCarSprite a subclass of BlueCarSprite, or both
Blue* and Green* to be subclasses of a CarSprite class that owns all the
methods. But without knowing more about your code, it's hard to say exactly.
--
Steven
More information about the Tutor
mailing list