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
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor

Reply via email to