Function object called through class attribute not working

I currently have a class system that records callbacks and then calls them when certain conditions are met. However, I am facing some problems storing the function object.

BUT:

class Foo(object): _callback = None @classmethod def Register(cls, fn): cls._callback = fn def Bar(): print "Called" Foo.Register(Bar) Foo._callback() 

 input clear Python 2.7.10 (default, Jul 14 2015, 19:46:27) [GCC 4.8.2] on linux Traceback (most recent call last): File "python", line 12, in <module> TypeError: unbound method Bar() must be called with Foo instance as first argument (got nothing instead) 

I'm not sure why he needs an instance of Foo when the function is not a member of Foo.

IN:

 class Foo(object): _callback = [] @classmethod def Register(cls, fn): cls._callback.append(fn) def Bar(): print "Called" Foo.Register(Bar) Foo._callback[0]() 

Why does the first version not work during the second version? What functionality is different when added to the list.

+7
python
source share
5 answers

Whenever you assign a function to a class object, it becomes a method:

 In [6]: Foo.baz = lambda self: 'baz' In [7]: f = Foo() In [8]: f.baz() Out[8]: 'baz' 

Note. This means that you can dynamically add methods to classes that will appear on instances that were created!

 In [9]: Foo.anothermethod = lambda self, x: x*2 In [10]: f.anothermethod(4) Out[10]: 8 

From docs :

If you still don’t understand how the methods work, look at maybe clarifying the situation. When an instance attribute refers to the fact that it is not a data attribute, its class is executed. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) an instance object and a function object just found together in an abstract object: this is a method object. When a method object is invoked with an argument list, a new argument list is created from the instance of an object and an argument list, and a function object is invoked with this new argument list.

+4
source share

When you assign Bar - cls._callback , _callback automatically becomes an unbound Foo method (since this is a function).

Therefore, when invoked, the instance is expected to become its first argument, which will result in failure when it does not transmit anything (expects an instance) or passes an instance ( Bar supports 0 arguments).

However, if you add Bar to the list instead, and then access the list item, you will have a normal-old-good Bar , ready to use, without any restrictions associated with it, because it is simply saved as an object , not a related method.

+4
source share

. Signing this up is simply adding it to the __dict__ classes, which stores references to related and unrelated methods (i.e. functions). Thus, by doing this, you simply add another reference to an unbound function that expects the instance to be the first argument.

Here is a simpler example:

 class Foo: test = lambda x: x Foo.test("Hello world!") 

Mistake:

 unbound method <lambda>(): must be called.... 
+3
source share

You still have a function object. However, calling a function from a class converts it to an unrelated method each time it is accessed through the class:

Note that the conversion from a functional object to a (unbound or related) method object occurs every time an attribute is retrieved from a class or instance.

(the above is described in the Python 2 Data Model Documentation user methods)

Therefore, it is required that the function that is now called through the class be passed an instance.

Note that unrelated methods exist only in Python 2, so your code is valid and will work in Python 3.

You can get a function object from an unrelated method by accessing its im_func attribute:

 Foo._callback.im_func() # Called 

The second case stores your function object in a container; it is not directly related to the class and can be retrieved using indexing and called as usual.

+3
source share

The documentation actually provides an example very similar to yours. In the description of "Method Objects" :

When an instance attribute is specified that is not a data attribute, its class is executed. If the name indicates a valid class attribute that is a function object, a method object is created by packing (pointers to) an instance object and a function object just found together in an abstract object: this is a method object.

and Random remarks :

There is no need for a function definition to be embedded in the text in the class definition: assigning the function object to a local variable in the class is also normal.

The last quote is followed by an example to which I refer.

Basically, the idea is that any function named in a class definition becomes a method of that class. Since you are not instantiating the class, your method is never bound to the instance object (hence ... unbound method Bar() ... ). Methods are always called with the first parameter containing the object to which they call, whether you like it or not.

That's why you, by the way, need a special decorator for @classmethod . The decorator wraps the function you created to give it the necessary interface, but pass the class object instead of the instance object to the wrapped function you specified.

Since your second version creates _callback as a data object, it simply saves the regular function pointer in the list and calls it as-is.

If you try to pass something like None to Foo._callback(None) , you will get rid of the original error that you get, but, of course, then there was no expectation of a transmission problem in the function parameter. You can work around this problem by providing Bar argument (which you ignore in this case):

 def Bar(self): print "Called" 
+2
source share

All Articles