簡體   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