In my Python app, I'm using events to communicate between different plugins. Now, instead of registering the methods to the events manually, I thought I might use decorators to do that for me.
I would like to have it look like this:
@events.lis开发者_如何转开发tento('event.name')
def myClassMethod(self, event):
...
I have first tried to do it like this:
def listento(to):
def listen_(func):
myEventManager.listen(to, func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
return listen_
When I callmyEventManger.listen('event', self.method)
from within the instance, everything is running fine. However, if I use the decorator approach, theself
argument is never passed.
The other approach that I have tried, after searching for a solution on the Internet, is to use a class as a decorator:
class listen(object):
def __init__(self, method):
myEventManager.listen('frontend.route.register', self)
self._method = method
self._name = method.__name__
self._self = None
def __get__(self, instance, owner):
self._self = instance
return self
def __call__(self, *args, **kwargs):
return self._method(self._self, *args, **kwargs)
The problem with this approach is that I don't really understand the concept of__get__
, and that I don't know how I'd incorporate the parameters.
Just for testing I have tried using a fixed event to listen to, but with this approach, nothing happens. When I add print statements, I can see that__init__
is called.
If I add an additional, "old style" event registration, both__get__
and__call__
get executed, and the event works, despite the new decorator.
What would be the best way to achieve what I'm looking for, or am I just missing some important concept with decorators?
The decorator approach isn't working because the decorator is being called when the class is constructed, not when the instance is constructed. When you say
class Foo(object):
@some_decorator
def bar(self, *args, **kwargs):
# etc etc
then some_decorator
will be called when the class Foo is constructed, and it will be passed an unbound method, not the bound method of an instance. That's why self
isn't getting passed.
The second method, on the other hand, could work as long as you only ever create one object of each class you use the decorator on, and if you're a bit clever. If you define listen
as above and then define
class Foo(object):
def __init__(self, *args, **kwargs):
self.some_method = self.some_method # SEE BELOW FOR EXPLANATION
# etc etc
@listen
def some_method(self, *args, **kwargs):
# etc etc
Then listen.__get__
would be called when someone tried to call f.some_method
directly for some f
...but the whole point of your scheme is that no-one's doing that! The event call back mechanism is calling the listen
instance directly 'cause that's what it gets passed and the listen
instance is calling the unbound method it squirrelled away when it was created. listen.__get__
won't ever get called and the _self
parameter is never getting set properly...unless you explicitly access self.some_method
yourself, as I did in the __init__
method above. Then listen.__get__
will be called upon instance creation and _self
will be set properly.
Problem is (a) this is a horrible, horrible hack and (b) if you try to create two instances of Foo
then the second one will overwrite the _self
set by the first, because there's still only one listen
object being created, and that's associated to the class, not the instance. If you only ever use one Foo
instance then you're fine, but if you have to have the event trigger two different Foo
's then you'll just have to use your "old style" event registration.
The TL,DR version: decorating a method decorates the unbound method of the class, whereas you want your event manager to get passed the bound method of an instance.
Part of your code is:
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
which defines wrapper
then completely ignores it and returns func
instead. Hard to say whether this is a real problem in your real code because obviously you're not posting that (as proven by typoes such as myEventManagre
, myEvnetManager
, &c), but if that's what you're doing in your actual code it is obviously part of your problem.
精彩评论