I currently have a system of classes that register callbacks and then call them when certain conditions are met. However I am running into some problems storing the function object.
A:
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 am not sure why it requires a Foo instance when the function is not a member of Foo.
B:
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 while the second version does? What functionality differs when adding it to a list instead.
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 you can dynamically add methods to classes, which will be visible on instantiated objects!
In [9]: Foo.anothermethod = lambda self, x: x*2
In [10]: f.anothermethod(4)
Out[10]: 8
From the docs:
If you still don’t understand how methods work, a look at the implementation can perhaps clarify matters. When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
When you assign Bar
to cls._callback
, _callback
becomes automatically an unbound method of Foo
(since it is a function).
Therefore, when called, it is expected to get the instance as its first argument, resulting in failure in cases where it is passed nothing (expects instance) or passed an instance (Bar
supports 0 arguments).
However, if you append Bar
to a list instead, and then access the list element, you have the normal-old-good Bar
ready for use without any bounding conventions hurting it - because it is merely conserved as an object, not a bound method.
.Assigning it is just adding it to the classes __dict__
which stores the references of the bound and unbound methods (ie functions). So, by doing this your just adding another reference to an unbound function which is expecting an instance as its first argument.
Here's an easier example:
class Foo:
test = lambda x: x
Foo.test("Hello world!")
Error:
unbound method <lambda>(): must be called....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With