繁体   English   中英

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

[英]Use an instance method as a decorator within another class

我正在尝试创建一个实例化串行对象的类( MySerial ),以便我可以写入/读取串行设备(UART)。 有一个实例方法是一个装饰器,它包含一个属于完全不同的类( App )的函数。 因此装饰器负责写入和读取串行缓冲区。

如果我在App类中创建一个MySerial实例,我就不能使用从MySerial创建的装饰器实例方法。 我已经尝试了上述实例方法并使用了第二个答案中解释的类方法,但我确实需要实例化MySerial ,因此使用__init__创建一个实例。

如何实现这一目标? 这不可能吗?

  • 创建一个作为实例方法的装饰器。
  • 在另一个类中使用此装饰器

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

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

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

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

将序列对象存储为类属性

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

class App():
    ser = MySerial()

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

在每个实例化上装饰

或者,如果您需要为每个App实例使用不同的MySerial实例,则需要等待创建实例以定义实例属性my_func 这意味着函数在每个实例创建时动态修饰,在这种情况下, @ decorator语法必须由函数调用替换。

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

    def myfunc(self):
        return 'yummy_bytes'

该解决方案通常用于装饰多个方法或有条件地停用序列化,例如在测试环境中。

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

有许多隐藏的陷阱使这成为一个冒险的设计,但它是一个很好的学习的例子。

首先,装饰时对“自我”的调用失败,因为在该范围内没有自我。 它只存在于方法中。 现在,简单的一个就不在了......

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来为实例获取唯一的命名空间。 这也是为什么你不能以这种方式获得实例独有的装饰器的原因。 考虑它的另一种方法是必须在生成实例之前定义Class。 因此,您无法在类的定义中使用实例。

装饰器需要以不存储任何实例属性的方式编写。 它将访问App实例属性。

class MySerial():
    def __init__(self):
        pass # Possibly don't need to have an __init__
    def write(self, serial_config):
        pass # write to buffer
    def read(self, serial_config):
        pass # read to buffer
    def decorator(self, func):
        def func_wrap(self_app: App, *args, **kwargs):
            self.write(func(self_app, *args, **kwars), self_app.serial_config)
            return self.read(self_app.serial_config)
        return func_wrap

ser = MySerial()

class App():
    def __init__(self, serial_config):
        self.serial_config = serial_config  # This is the instance data for     MySerial

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

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

现在我假设MySerial将有一个唯一的文件,或端口或每个App实例的东西。 这是将在serial_config中记录的内容。 如果流正在关闭,这可能不是很优雅,但您应该能够针对您的确切应用进行改进。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM