Python - function as class attribute becomes a bou

2019-04-19 19:23发布

问题:

I noticed that if I define a class attribute equal to a function when I create an instance of that class the attribute becomes a bound method. Can someone explain me the reason of this behaviour?

In [9]: def func():
   ...:     pass
   ...: 

In [10]: class A(object):
   ....:     f = func
   ....:     

In [11]: a = A()

In [12]: a.f
Out[12]: <bound method A.func of <__main__.A object at 0x104add190>>

In [13]: a.f()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-19134f1ad9a8> in <module>()
----> 1 a.f()
    global a.f = <bound method A.func of <__main__.A object at 0x104add190>>

TypeError: func() takes no arguments (1 given)

回答1:

You asigned a function to the attribute f. Cause the function is now inside a class it becomes a method, specifically a bound method (cause it's bounded to the object, further explanation here).

Methods in classes receive at least one parameter (self), unless it's specifically told to not - see class methods and static methods -, for this reason the error says that func takes no arguments (as it's defined as def func():) but received 1 (self)

To do what you want, you should tell python that you're using a static method

def func():
    pass

class A(object):
    f = staticmethod(func)


回答2:

Python is not a message based OO system1. Instead, similar to JavaScript, properties are resolved to first-class functions and then invoked; the behavior differs a bit in the mechanics of such, as discovered.

In Python the requirement is that methods have at least one parameter, normally called self, that will be automatically supplied the associated instance when it is invoked as a method.

Furthermore (and perhaps to the point of the question), Python does not differentiate between using def f.. or f = some_func() when establishing instance member bindings; arguably this matches behavior outside of classes.

In the example, assigning the function to the instance 'makes it expect to be treated like an instance method'. It is the exact same - parameterless - function called in both cases; only the future usage of such is relevant.

Now, unlike JavaScript, Python handles methods and object association through the concept of bound methods - functions resolved as methods are always 'bound'.

The behavior of a.f returning a bound method - function that will automatically supply the bound object to the first parameter as self - is done independently of the source of the function. In this case that means the parameterless function cannot be used when it is 'bound' as it does not accept a self parameter.

As a demonstration, the following will fail in the same way because the source underlying method does not meet the minimum requirements of accepting the instance as an argument:

g = a.f
g()

In this case calling g() is equivalent to calling func(a).


1 For comparison, Java, C#, Ruby, and SmallTalk are message based OO systems - in these an object is told to invoke a method by a 'name', instead of resolving a method (or function) as a value which can be invoked.