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

2024-05-21 00:29:02 发布

您现在位置:Python中文网/ 问答频道 /正文

Python decorators使用起来很有趣,但由于参数传递给decorators的方式,我似乎遇到了麻烦。在这里,我将decorator定义为基类的一部分(decorator将访问类成员,因此它将需要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

我再次省略了函数实现,因为它们不相关。

简而言之,问题是,虽然我可以通过将基类中定义的decorator指定为SubSystem.UpdateGUI来从继承的类访问它,但在尝试使用它时,我最终会得到这个TypeError:

unbound method UpdateGUI() must be called with SubSystem instance as first argument (got function instance instead)

这是因为我没有立即将self参数传递给decorator的可识别方法!

有办法吗?或者我达到了Python中当前decorator实现的极限了吗?


Tags: nameselfdecorators定义valueportdefargs
3条回答

你已经回答了这个问题:如果你调用SubSystem.UpdateGUI,你希望得到什么样的参数作为self?没有明显的实例应该传递给decorator。

你可以做几件事来解决这个问题。也许你已经有了一个在其他地方实例化的subSystem?然后你可以用它的装饰器:

subSystem = SubSystem()
subSystem.UpdateGUI(...)

但也许你一开始就不需要这个实例,只需要SubSystem?在这种情况下,使用classmethoddecorator告诉Python该函数应该将其类作为第一个参数而不是实例来接收:

@classmethod
def UpdateGUI(cls,...):
    ...

最后,也许您不需要访问实例或类!在这种情况下,使用staticmethod

@staticmethod
def UpdateGUI(...):
    ...

哦,顺便说一下,Python约定是为类保留CamelCase名称,并为该类上的方法使用mixedCase或under_scored名称。

你需要让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

将decorator从SubSytem类中拉出可能更容易: (注意,我假设调用setportself与您希望用来调用updateGUIFieldself相同。)

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')

相关问题 更多 >