[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