简体   繁体   中英

Calling member methods from class object in python 3

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM