python classmethods are not callable from class.__dict__
Asked Answered
R

1

7

I am learning the concept of classmethods in python.

class A():
    n=0
    # object method
    def func_o(self):
        self.n += 1
        print self.n

    # well, class method
    @classmethod
    def func_c(cls):
        cls.n += 1
        print cls.n

when checking the callable() attributes of the class, I came across with this peculiar output:

>>> [(k, callable(v)) for k,v in A.__dict__.items()]
[('__module__', False), ('__doc__', False), ('func_o', True), ('func_c', False), ('n', False)] 

('func_o', True) even though the class __dict__ was inspected, similarly ('func_c', False) for some reason.

Can someone explain?

Retard answered 17/1, 2017 at 11:54 Comment(1)
See also: stackoverflow.com/questions/32284275Cattima
R
10

A classmethod object is not a function object, no. It is not meant to be callable.

classmethod objects are descriptors; descriptors facilitate the binding of an object to a specific instance or class. Functions and properties are also descriptors; binding produces methods, or the property value, respectively. See the Python Descriptor How To. If you were to access a classmethod descriptor on the class, then this triggers a classmethod.__get__(None, cls) call that produces a bound method (which, when called, invokes the original function with the class object passed in as the first argument).

When you access all class attributes through the __dict__ attribute, you are bypassing the descriptor protocol, so you get the raw descriptor objects themselves.

Either access the object on the class (and thus trigger the descriptor.__get__(None, cls) call that binds class methods to the class object), manually bind, or test explicitly for classmethod objects:

>>> A.__dict__['func_c']
<classmethod object at 0x1018e6cc8>
>>> A.__dict__['func_c'].__get__(None, A)  # explicitly bind to a class
<bound method classobj.func_c of <class __main__.A at 0x101c52328>>
>>> callable(A.__dict__['func_c'].__get__(None, A))
True
>>> A.func_c    # trigger the protocol and bind to a class
<bound method classobj.func_c of <class __main__.A at 0x101c52328>>

You can also access the original function that the classmethod object wraps, using the __func__ attribute:

>>> A.__dict__['func_c'].__func__
<function func_c at 0x101c59668>

which is, of course, itself callable too.

Note that this all applies to staticmethod objects too; a staticmethod object, when bound, just returns the original function.

Rameau answered 17/1, 2017 at 11:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.