Decoration method

In my Python application, I use events to communicate between different plugins. Now, instead of manually registering methods for events, I thought that I could use decorators for this.

I would like it to look like this:

@events.listento('event.name') def myClassMethod(self, event): ... 

At first I tried to do it as follows:

 def listento(to): def listen_(func): myEventManager.listen(to, func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return func return listen_ 

When I call myEventManger.listen('event', self.method) from the instance, everything works fine. However, if I use the decorator approach, the self argument is never passed.

Another approach that I tried, after searching for a solution on the Internet, was to use the 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 really do not understand the concept of __get__ and that I do not know how to enable the parameters. Just for testing, I tried to use a fixed event for listening, but with this approach nothing happens. When I add print instructions, I see that __init__ is being called. If I add additional logging of "old-style" events, both __get__ and __call__ , and this 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?

+4
source share
2 answers

The decorator approach does not work, because the decorator is called when the class is built, and not when the instance is created. When you say

 class Foo(object): @some_decorator def bar(self, *args, **kwargs): # etc etc 

then some_decorator will be called when the Foo class is built, and the unbound method, and not the associated instance method, will be passed to it. That is why self does not work.

The second method, on the other hand, can work as long as you create only one object of each class on which you use the decorator, and if you are a little smart. If you define listen as above, 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__ will be called when someone tried to call f.some_method directly for some f ... but the whole point of your circuit is that no one does this! The event callback mechanism calls the listen instance directly, "because it is passed, and the listen instance calls the unbound method that it scrolled when it was created. listen.__get__ will never be called, and the _self parameter _self never be set correctly ... unless you explicitly get access to self.some_method yourself, as was the case with the __init__ method listen.__get__ is called when the instance is created, and _self will be installed correctly.

The problem is that (a) it is a terrible, terrible hack and (b) if you try to create two instances of Foo and then the second will overwrite the _self installed by the first, because there is still only one listen created object and associated with it is a class, not an instance. If you ever use only one instance of Foo , then you are fine, but if you need an event trigger to have two different Foo , you just need to use the "old style" event logging.

Version TL, DR: decorating a method decorates an unrelated class method, while you want your event manager to pass in an associated instance method.

+4
source

Part of your code:

  def wrapper(*args, **kwargs): return func(*args, **kwargs) return func 

which defines a wrapper , then completely ignores it and returns func . It's hard to say if this is a real problem in your real code, because obviously you are not myEventManagre it (as verified by print shops like myEventManagre , myEvnetManager , & c), but if that's what you are doing in your actual code obviously is part of your problem.

+3
source

All Articles