Python 装饰一种方法
在我的Python应用程序中,我使用事件在不同插件之间进行通信。 现在,我不想手动将方法注册到事件中,我想我可以使用decorator来完成这项工作 我希望它看起来像这样:Python 装饰一种方法,python,methods,decorator,Python,Methods,Decorator,在我的Python应用程序中,我使用事件在不同插件之间进行通信。 现在,我不想手动将方法注册到事件中,我想我可以使用decorator来完成这项工作 我希望它看起来像这样: @events.listento('event.name') def myClassMethod(self, event): ... def listento(to): def listen_(func): myEventManager.listen(to, func) de
@events.listento('event.name')
def myClassMethod(self, event):
...
def listento(to):
def listen_(func):
myEventManager.listen(to, func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
return listen_
我首先尝试这样做:
@events.listento('event.name')
def myClassMethod(self, event):
...
def listento(to):
def listen_(func):
myEventManager.listen(to, func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
return listen_
当我从实例中调用myEventManger.listen('event',self.method)
时,一切都正常运行。但是,如果使用decorator方法,则永远不会传递self
参数
在互联网上搜索解决方案后,我尝试的另一种方法是使用类作为装饰器:
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)
这种方法的问题是,我不太理解\uuuu get\uuu
的概念,也不知道如何合并参数。
为了进行测试,我尝试使用固定的事件来监听,但是使用这种方法,什么也没发生。当我添加print语句时,我可以看到调用了\uuuu init\uuuu
。
如果我添加一个额外的“旧式”事件注册,则执行\uuuuuu get\uuuuuu
和\uuuuu call\uuuuuuuuu
这两个事件,尽管使用了新的装饰器,但事件仍然有效
实现我想要的目标的最佳方法是什么,或者我只是缺少了装饰器的一些重要概念?装饰器方法不起作用,因为装饰器是在构造类时调用的,而不是在构造实例时调用的。当你说
class Foo(object):
@some_decorator
def bar(self, *args, **kwargs):
# etc etc
然后,在构造类Foo时,将调用某个_decorator,并将向它传递一个未绑定的方法,而不是实例的绑定方法。这就是为什么self
没有通过考试的原因
另一方面,第二种方法可以工作,只要您对使用装饰器的每个类只创建一个对象,并且您稍微聪明一点。如果您定义请按上述方法听
,然后定义
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
然后听。当有人试图调用f时,会调用\uu get\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu!事件回调机制直接调用listen
实例,因为这就是它传递的内容,而listen
实例正在调用创建时存储的未绑定方法<代码>侦听。\uuuu get\uuuuuu
永远不会被调用,\u self
参数永远不会正确设置……除非您显式访问self。一些方法是您自己
的,就像我在上面的\uuuu init\uuuuuuuu
方法中所做的那样。然后,listen.\uuuu get\uuuu
将在创建实例时被调用,并且\u self
将被正确设置
问题是(a)这是一个可怕的黑客行为,(b)如果您尝试创建两个Foo
实例,那么第二个实例将覆盖第一个实例设置的\u self
,因为仍然只有一个listen
对象被创建,并且该对象与类关联,而不是与实例关联。如果您只使用一个Foo
实例,那么您就可以了,但是如果您必须让事件触发两个不同的Foo
,那么您只需要使用“旧式”事件注册
TL,DR版本:装饰方法装饰类的未绑定方法,而您希望事件管理器通过实例的绑定方法。
< P>代码的一部分是:
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
它定义了wrapper
,然后完全忽略它并返回func
。很难说这在您的真实代码中是否是一个真正的问题,因为显然您没有发布这一问题(如myEventManagre
,myEvnetManager
,&c等打字错误证明了这一点),但是,如果这是您在实际代码中所做的,那么显然是您的问题的一部分。您确定在定义了方法之后,只调用myEventManager.listen('frontend.route.register',myClassMethod')
不会更高兴吗?由于您根本没有修改该方法的行为,我不确定decorator是否最适合这里。我希望直接从方法定义中看到哪个方法注册到哪个事件。我刚刚为事件注册发明了一些东西,所以很清楚这是什么意思:)抱歉,写得太草率,当时很早。。。无论如何,你是对的,这是一个我没有发现的实际错误(wrapper vs func)。但是,最后它没有什么区别,因为我根本不想改变方法,所以我可以完全省略包装器。啊,我想我现在明白了。非常感谢您的详细解释!所以没有什么,嗯,让我们称之为正确的方式,我在寻找什么?我不知道。如果您仅以f的身份访问方法,您发现的“类作为具有\uuu get\uuuu
的decorator”技巧会在某种程度上装饰一个方法。有些方法()
,但这种技术有局限性,您第一次尝试时恰好发现了其中一种。