代码之家  ›  专栏  ›  技术社区  ›  Aphex Bodhi Hu

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

  •  22
  • Aphex Bodhi Hu  · 技术社区  · 14 年前

    Python decorator使用起来很有趣,但由于参数传递给decorator的方式,我似乎遇到了麻烦。这里我有一个定义为基类一部分的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
    

    简而言之,问题在于,虽然我可以通过将基类中定义的装饰器指定为SubSystem.UpdateGUI文件,我在尝试使用它时最终得到了这个类型错误:

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

    这是因为我没有一种可以立即识别的方式通过 self 参数到装饰器!

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

    4 回复  |  直到 10 年前
        1
  •  24
  •   kennytm    14 年前

    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
    
        2
  •  3
  •   unutbu    14 年前

    把装潢师拉出来可能更容易 SubSytem 班级: self 这叫 setport 自己 你想用来打电话的 updateGUIField

    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')
    
        3
  •  3
  •   Katriel    14 年前

    你在问这个问题的时候已经回答了这个问题:你希望得到什么样的论证 self 如果你打电话 SubSystem.UpdateGUI ? 没有一个明显的实例应该传递给decorator。

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

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

    但也许你一开始并不需要这个实例,只是 SubSystem classmethod decorator告诉Python此函数应将其类作为第一个参数而不是实例接收:

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

    staticmethod :

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

        4
  •  2
  •   aaronasterling    14 年前

    您需要使用的实例 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接口,那么使用一个类方法,并使decorator访问的所有内容都成为一个类实例。

    如果它们可以有不同的接口,您需要决定是否要用继承来表示不同性(在这种情况下,您还将使用 分类方法 并调用 子系统