[英]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
调用所需的write
和read
方法。 另请注意,在声明中, 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.