[英]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.