pythonmetaclassclass-method

How does a classmethod object work?


I'm having trouble to understand how a classmethod object works in Python, especially in the context of metaclasses and in __new__. In my special case I would like to get the name of a classmethod member, when I iterate through the members that were given to __new__.

For normal methods the name is simply stored in a __name__ attribute, but for a classmethod there is apparently no such attribute. I don't even see how the classmethod is invoked, as there is no __call__ attribute either.

Can someone explain to me how a classmethod works or point me to some documentation? Googling led me nowhere. Thanks!


Solution

  • A classmethod object is a descriptor. You need to understand how descriptors work.

    In a nutshell, a descriptor is an object which has a method __get__, which takes three arguments: self, an instance, and an instance type.

    During normal attribute lookup, if a looked-up object A has a method __get__, that method gets called and what it returns is substituted in place for the object A. This is how functions (which are also descriptors) become bound methods when you call a method on an object.

    class Foo(object):
         def bar(self, arg1, arg2):
             print arg1, arg2
    
    foo = Foo()
    # this:
    foo.bar(1,2)  # prints '1 2'
    # does about the same thing as this:
    Foo.__dict__['bar'].__get__(foo, type(foo))(1,2)  # prints '1 2'
    

    A classmethod object works the same way. When it gets looked up, its __get__ method gets called. The __get__ of a classmethod discards the argument corresponding to the instance (if there was one) and only passes along the instance_type when it calls __get__ on the wrapped function.

    A illustrative doodle:

    In [14]: def foo(cls):
       ....:     print cls
       ....:     
    In [15]: classmethod(foo)
    Out[15]: <classmethod object at 0x756e50>
    In [16]: cm = classmethod(foo)
    In [17]: cm.__get__(None, dict)
    Out[17]: <bound method type.foo of <type 'dict'>>
    In [18]: cm.__get__(None, dict)()
    <type 'dict'>
    In [19]: cm.__get__({}, dict)
    Out[19]: <bound method type.foo of <type 'dict'>>
    In [20]: cm.__get__({}, dict)()
    <type 'dict'>
    In [21]: cm.__get__("Some bogus unused string", dict)()
    <type 'dict'>
    

    More info on descriptors can be found here (among other places): http://users.rcn.com/python/download/Descriptor.htm

    For the specific task of getting the name of the function wrapped by a classmethod:

    In [29]: cm.__get__(None, dict).im_func.__name__
    Out[29]: 'foo'