Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python classmethods are not callable from class.__dict__

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?

like image 418
Gil Avatar asked Jan 17 '17 11:01

Gil


People also ask

How to fix object is not callable Python?

But in Python, this would lead to the Typeerror: int object is not callable error. To fix this error, you need to let Python know you want to multiply the number outside the parentheses with the sum of the numbers inside the parentheses. Python allows you to specify any arithmetic sign before the opening parenthesis.

What is Classmethod?

The classmethod() is an inbuilt function in Python, which returns a class method for a given function.; Syntax: classmethod(function) Parameter :This function accepts the function name as a parameter. Return Type:This function returns the converted class method.


1 Answers

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.

like image 72
Martijn Pieters Avatar answered Oct 23 '22 10:10

Martijn Pieters