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
...
我省略了其余的实现。现在这个类是将继承它的各种SubSystems的基类 - 一些继承的类需要使用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来从继承类访问基类中定义的装饰器,但在尝试使用它时我最终会得到这个TypeError:
unbound method UpdateGUI() must be called with SubSystem instance as first argument (got function instance instead)
这是因为我没有立即可识别的方法将self
参数传递给装饰器!
有办法做到这一点吗?或者我在Python中达到了当前装饰器实现的限制?
答案 0 :(得分:20)
您需要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
答案 1 :(得分:3)
将装饰器拉出SubSytem
类可能更容易:
(请注意,我假设调用self
的{{1}}与您希望用来调用setport
的{{1}}相同。)
self
答案 2 :(得分:3)
你有点回答这个问题:如果你致电self
,你期望获得SubSystem.UpdateGUI
的论点?没有明显的实例应该传递给装饰器。
你可以做几件事来解决这个问题。也许你已经在其他地方实例化了subSystem
?然后你可以使用它的装饰器:
subSystem = SubSystem()
subSystem.UpdateGUI(...)
但也许你首先不需要实例,只是类 SubSystem
?在这种情况下,使用classmethod
装饰器告诉Python该函数应该接收其类作为第一个参数而不是实例:
@classmethod
def UpdateGUI(cls,...):
...
最后,也许你不需要访问实例或类!在这种情况下,请使用staticmethod
:
@staticmethod
def UpdateGUI(...):
...
哦,顺便说一句,Python约定是为类保留CamelCase名称,并为该类的方法使用mixedCase或under_scored名称。
答案 3 :(得分:2)
您需要使用SubSystem
的实例进行装饰,或使用classmethod
作为肯尼建议。
subsys = SubSystem()
class DO(SubSystem):
def getport(self, port):
"""Returns the value of Digital Output port "port"."""
pass
@subsys.UpdateGUI
def setport(self, port, value):
"""Sets the value of Digital Output port "port"."""
pass
您可以通过决定是否希望所有子类实例共享相同的GUI界面,或者您希望能够让不同的实例具有不同的接口来决定做什么。
如果它们共享相同的GUI界面,请使用类方法并使装饰器访问类实例的所有内容。
如果它们可以具有不同的接口,则需要确定是否要使用继承来表示不同性(在这种情况下,您还将使用classmethod
并在SubSystem
的子类上调用装饰器)或者如果它更好地表示为不同的实例。在这种情况下,为每个接口创建一个实例,并在该实例上调用装饰器。