I have this bit of code:
class ABC:
def test():
print('Hi there')
ABC.test()
Which outputs:
Hi there
Whereas this fails:
ABC().test()
TypeError: test() takes 0 positional arguments but 1 was given
# Makes sense
We know that when we invoke a classmethod
like <classname>.<methodname>()
, the class is implicitly passed as an argument to it, but test
here takes no arguments.
Why don't we see a TypeError
because of the unexpected argument? Isn't the class passed as an argument to test
?
Nothing is passed to test()
because functions do not bind to anything when accessed to a class. It remains unbound , you get the original function:
>>> class ABC:
... def test():
... print('Hi there')
...
>>> ABC.test
<function ABC.test at 0x1082706c0>
You can call functions directly, provided you pass in the right number of arguments. Here that is 0, so ABC.test()
succeeds.
It is not a classmethod
, that would require decorating it with @classmethod
(or otherwise producing a classmethod
object and storing it as an attribute of the class), at which point accessing the attribute binds the function to the class object, producing a bound method
:
>>> class ABC:
... @classmethod
... def test():
... print('Hi there')
...
>>> ABC.test
<bound method ABC.test of <class '__main__.ABC'>>
Calling this results in an exception:
>>> ABC.test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: test() takes 0 positional arguments but 1 was given
Functions can be bound too, but only when accessed on an instance of the class. Both function
and classmethod
objects implement the descriptor protocol , and accessing attributes on classes and on instances will trigger the __get__
method on descriptors. But only classmethod
objects return a bound method in that case, functions just return themselves.
Specifically, the descriptor __get__
method is passed None
for the first argument, the object to bind to, when the attribute access is on a class, and the instance when accessed on the instance. classmethod().__get__()
ignores the instance argument and produces a method object that binds to the second argument ( owner
). Functions return self
when the first argument is None
, or a method object that binds to the instance otherwise:
>>> def foo(boundto='nothing'):
... print(f'Bound to {boundto}')
...
>>> foo.__get__('instance', 'owner')()
Bound to instance
>>> foo.__get__(None, 'owner')()
Bound to nothing
>>> classmethod(foo).__get__('instance', 'owner')()
Bound to owner
>>> classmethod(foo).__get__(None, 'owner')()
Bound to owner
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.