Question

In Data Model of the reference, writer spent lots of effort explaining how User-defined methods are created and operated:(See http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy and roll down)

User-defined method objects may be created when getting an attribute of a class (perhaps via an in- stance of that class), if that attribute is a user-defined function object, an unbound user-defined method object, or a class method object. When the attribute is a user-defined method object, a new method object is only created if the class from which it is being retrieved is the same as, or a derived class of, the class stored in the original method object; otherwise, the original method object is used as it is.

So what's the difference between an unbound user-defined method object and a class method object?

Was it helpful?

Solution

From the "user" point of view, a class method in Python is a method that receives its class as its first parameter - unlike "ordinary" methods which receive an instance of the class as its first parameter - which by convention is called self.

If you retrieve an "ordinary" method from a class, instead of from an instace of that class, you get an "unbound method" - i.e. an object that is a wrapper around a function, but that does not automatically adds either the class itself, nor any instance as the first parameter when it is called. Threfore if you are to call the "unbound method" you have to manually pass an instance of its class as its first parameter.

If you manually call a class method, on the other hand, the class is filled in as the first parameter for you:

>>> class A(object):
...   def b(self):
...      pass
...   @classmethod
...   def c(cls):
...      pass
... 
>>> A.b
<unbound method A.b>
>>> A.c
<bound method type.c of <class '__main__.A'>>
>>> A.c()
>>> A.b()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method b() must be called with A instance as first argument (got nothing instead)
>>> 

Under the hood what goes is more or less like this - with "new style classes":

When one defines a class body, the methods are just ordinary functions - when the class body is over, Python calls the class's metaclass (which ordinarily is the builtin type ) - and pass to it as parameters the name, base classes, and class body dictionary. This call yields a class - which in Python is, an object which is a class, since everything is an object.

Now, Python has some nifty ways of customizing attribute access - the so called "descriptors". A descriptor is any object that defines a method named __get__ (or __set__ or __del__ but we don't care about those here). When one access an attribute of a class or object in Python, the object referred by that attribute is returned - except if it is a class attribute, and the object is a descriptor. In that case, instead of returning the object itself, Python calls the __get__ method on that object, and returns its results instead. For example, the property built-in is just a class that implements both __set__, __get__ and __del__ as appropriate.

Now, what happens when the attribute is retrieved, is that any function (or class method or unbound method, as the data model states) on its body, does have a __get__ method, which makes it a descriptor. Basically, a descriptor that at each attribute access to retrieve the object named as the function as it is defined on the function body, creates a new object around that function - an object that when called will have the first parameter automatically filled in - which is to say, a method.

Ex.:

>>> class B(object):
...    def c(self):
...      pass
...    print c
... 
<function c at 0x1927398>
>>> print B.c
<unbound method B.c>
>>> b = B()
>>> b.c
<bound method B.c of <__main__.B object at 0x1930a10>

If you want to retrieve the function object, without conversion to a method object, you can do so through the class's __dict__ attribute, which does not trigger the descriptor:

>>> B.__dict__["c"]
<function c at 0x1927398>
>>> B.__dict__["c"].__get__
<method-wrapper '__get__' of function object at 0x1927398>
>>> B.__dict__["c"].__get__(b, B)
<bound method B.c of <__main__.B object at 0x1930a10>>
>>> B.__dict__["c"].__get__(None, B)
<unbound method B.c>

As for "class methods", these are just different type of objects, which are explicitly decorated with the builtin classmethod - The object it returns when its __get__ is called is a wrapper around the original function that will fill in the cls as the first parameter on call.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top