基础类的Python装饰器无法装饰继承类中的成员函数
Python中的装饰器使用起来很有趣,但我似乎遇到了一个麻烦,主要是因为传递给装饰器的参数方式。这里我定义了一个装饰器,它是一个基类的一部分(这个装饰器需要访问类的成员,所以它需要一个self参数)。
class SubSystem(object):
def UpdateGUI(self, fun): #function decorator
def wrapper(*args):
self.updateGUIField(*args)
return fun(*args)
return wrapper
def updateGUIField(self, name, value):
if name in self.gui:
if type(self.gui[name]) == System.Windows.Controls.CheckBox:
self.gui[name].IsChecked = value #update checkbox on ui
elif type(self.gui[name]) == System.Windows.Controls.Slider:
self.gui[name].Value = value # update slider on ui
...
我省略了其余的实现部分。现在这个类是各种子系统的基类,这些子系统会从它继承而来——其中一些继承的类需要使用UpdateGUI装饰器。
class DO(SubSystem):
def getport(self, port):
"""Returns the value of Digital Output port "port"."""
pass
@SubSystem.UpdateGUI
def setport(self, port, value):
"""Sets the value of Digital Output port "port"."""
pass
我再次省略了函数的具体实现,因为它们并不相关。
简单来说,问题在于,虽然我可以通过指定SubSystem.UpdateGUI来从继承的类访问基类中定义的装饰器,但当我尝试使用它时,最终会出现这个类型错误:
未绑定的方法UpdateGUI()必须以SubSystem实例作为第一个参数调用(而不是函数实例)
这是因为我没有明显的方法来将self
参数传递给装饰器!
有没有办法解决这个问题?还是说我已经碰到了Python当前装饰器实现的限制?
4 个回答
把装饰器从SubSytem
类中提取出来可能会更简单一些:
(注意,我假设调用setport
的self
和你想用来调用updateGUIField
的self
是同一个。)
def UpdateGUI(fun): #function decorator
def wrapper(self,*args):
self.updateGUIField(*args)
return fun(self,*args)
return wrapper
class SubSystem(object):
def updateGUIField(self, name, value):
# if name in self.gui:
# if type(self.gui[name]) == System.Windows.Controls.CheckBox:
# self.gui[name].IsChecked = value #update checkbox on ui
# elif type(self.gui[name]) == System.Windows.Controls.Slider:
# self.gui[name].Value = value # update slider on ui
print(name,value)
class DO(SubSystem):
@UpdateGUI
def setport(self, port, value):
"""Sets the value of Digital Output port "port"."""
pass
do=DO()
do.setport('p','v')
# ('p', 'v')
你在提问的时候其实已经部分回答了问题:如果你调用 SubSystem.UpdateGUI
,你希望 self
得到什么参数呢?这里没有明显的实例可以传给装饰器。
不过,你可以采取几种方法来解决这个问题。也许你在别的地方已经创建了一个 subSystem
的实例?那你可以使用它的装饰器:
subSystem = SubSystem()
subSystem.UpdateGUI(...)
但是,也许你根本不需要实例,只需要 类 SubSystem
?在这种情况下,可以使用 classmethod
装饰器来告诉 Python,这个函数应该接收它的类作为第一个参数,而不是实例:
@classmethod
def UpdateGUI(cls,...):
...
最后,也许你根本不需要访问实例或类!在这种情况下,可以使用 staticmethod
:
@staticmethod
def UpdateGUI(...):
...
顺便提一下,Python 的约定是将 CamelCase 命名保留给类,而对类的方法则使用混合大小写或下划线命名。
你需要把 UpdateGUI
改成一个 @classmethod
,并让你的 wrapper
知道 self
是什么。下面是一个可以正常工作的例子:
class X(object):
@classmethod
def foo(cls, fun):
def wrapper(self, *args, **kwargs):
self.write(*args, **kwargs)
return fun(self, *args, **kwargs)
return wrapper
def write(self, *args, **kwargs):
print(args, kwargs)
class Y(X):
@X.foo
def bar(self, x):
print("x:", x)
Y().bar(3)
# prints:
# (3,) {}
# x: 3