Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - function as class attribute becomes a bound method

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)
like image 847
se7entyse7en Avatar asked Feb 10 '16 17:02

se7entyse7en


2 Answers

You assigned a function to the attribute A.f (the attribute f of the class A). The attribute A.f was defined as part of the class. It is a function, so it is by default an instance method of that class.

Creating an instance (named a) of class A causes that instance to have an attribute f, and you access that by the name a.f. This is a bound method (cause it's bounded to the object a; further explanation here).

Every instance method, when it is called, automatically receives the instance as its first argument (conventionally named self). Other types of method are possible: - 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)
like image 124
Mr. E Avatar answered Nov 10 '22 10:11

Mr. E


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.

like image 32
user2864740 Avatar answered Nov 10 '22 10:11

user2864740