在另一个类中使用实例方法作为装饰器

2024-06-26 17:48:46 发布

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

我试图创建一个类(MySerial),它实例化一个串行对象,这样我就可以对串行设备(UART)进行写/读。有一个实例方法是一个decorator,它包装了一个属于完全不同类(App)的函数。所以decorator负责对串行缓冲区的写入和读取。在

如果我在App类中创建MySerial的实例,则不能使用从MySerial创建的decorator实例方法。 {{1}因此,我尝试使用^ cd1>方法创建一个实例。在

如何做到这一点?不可能吗?在

  • 创建一个实例方法的decorator。在
  • 在其他类中使用此修饰符

class MySerial():
    def __init__(self):
        pass # I have to have an __init__
    def write(self):
        pass # write to buffer
    def read(self):
        pass # read to buffer
    def decorator(self, func):
        def func_wrap(*args, **kwargs):
            self.write(func(*args, **kwars))
            return self.read()
        return func_wrap

class App():
    def __init__(self):
        self.ser = MySerial()

    @self.ser.decorator  # <-- does not work here.
    def myfunc(self):
        # 'yummy_bytes' is written to the serial buffer via 
        # MySerial's decorator method
        return 'yummy_bytes'

if __name__ == '__main__':
    app = App()

Tags: to实例方法selfappreadreturninit
3条回答

有很多隐藏的陷阱使得这是一个危险的设计,然而这是一个很好的学习例子。在

首先,在装饰时调用“self”失败,因为在该范围内没有self。它只存在于方法内部。既然最简单的那个已经不碍事了。。。在

myfunc是App类的属性。当你创建一个App实例时,总是调用一个函数。只有当这种情况发生的时候,才是有条理的。在

a1 = App()
a2 = App()
assert a1.myfunc.__func__ is a2.myfunc.__func__
assert id(a1.myfunc) is id(a2.myfunc)  # Methods have some weirdness that means that won't equate but id's show they are the same 

这就是为什么需要self为实例获取唯一的命名空间。这也是为什么您不能以这种方式获得实例所特有的decorator。 另一种考虑方法是,在生成实例之前必须定义类。因此,在类的定义中不能使用实例。在

解决方案

decorator需要以一种不存储任何实例属性的方式编写。它将访问应用程序实例属性。在

^{pr2}$

现在我假设MySerial将为每个App实例提供一个唯一的文件、端口或其他东西。这是串行配置中记录的内容。如果流是打开一个关闭的流,这可能不太优雅,但是您应该能够为您的确切应用程序改进这一点。在

这不起作用的原因是因为您引用的是类主体中的self,在这里没有定义它。这里有两个解决方案。在

将串行对象存储为类属性

如果您将MySerial实例存储为属性,则可以在类主体中访问它:

class App():
    ser = MySerial()

    @ser.decorator
    def myfunc(self):
        return 'yummy_bytes'

在每个实例上装饰

或者,如果每个App实例都需要一个不同的MySerial实例,那么您需要等待创建实例来定义一个实例属性my_func。这意味着在每次创建实例时都会动态地修饰函数,在这种情况下,@修饰符语法必须由函数调用替换。在

^{pr2}$

此解决方案可概括为装饰多个方法或有条件地取消序列化,例如在测试环境中。在

import env

class App():
    def __init__(self):
        self.ser = MySerial()

        to_decorate = [] if env.test else ['myfunc']

        for fn_name in to_decorate:
            fn = getattr(self, fn_name)
            setattr(self, fn_name, self.ser.decorator(fn))

您可以使用staticmethod来包装decoratordecorator的内部func_wrap函数在其签名中包含一个附加参数:clscls可用于访问App实例的ser属性,然后可以从cls.ser调用所需的方法write和{}。另外,请注意,在声明中,MySerial.write不接受参数,而是传递包装函数的结果。下面的代码使用*args来防止{},否则将引发{}:

class MySerial():
   def __init__(self):
     pass # I have to have an __init__
   def write(self, *args):
     pass # write to buffer
   def read(self):
     pass # read to buffer
   @staticmethod
   def decorator(func):
     def func_wrap(cls, *args, **kwargs):
        cls.ser.write(func(cls, *args, **kwargs))
        return cls.ser.read()
     return func_wrap

class App():
  def __init__(self):
     self.ser = MySerial()
  @MySerial.decorator 
  def myfunc(self):
    # 'yummy_bytes' is written to the serial buffer via 
    # MySerial's decorator method
    return 'yummy_bytes'

App().myfunc()

相关问题 更多 >