作为基类一部分的Python装饰器不能用于装饰继承类中的成员函数

时间:2010-09-23 19:53:14

标签: python inheritance ironpython decorator

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中达到了当前装饰器实现的限制?

4 个答案:

答案 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的子类上调用装饰器)或者如果它更好地表示为不同的实例。在这种情况下,为每个接口创建一个实例,并在该实例上调用装饰器。