Используйте метод экземпляра в качестве декоратора в другом классе - PullRequest
0 голосов
/ 01 июня 2018

Я пытаюсь создать класс (MySerial), который создает экземпляр последовательного объекта, чтобы я мог записывать / читать на последовательное устройство (UART).Существует метод экземпляра, который является декоратором, который оборачивает функцию, которая принадлежит совершенно другому классу (App).Поэтому декоратор отвечает за запись и чтение в последовательный буфер.

Если я создаю экземпляр MySerial внутри класса App, я не могу использовать метод экземпляра декоратора, созданный из 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()

Ответы [ 3 ]

0 голосов
/ 01 июня 2018

Причина, по которой это не работает, заключается в том, что вы ссылаетесь на self в теле класса, где оно не определено.Вот два решения:

Сохраните серийный объект как атрибут класса

Если вы сохраните экземпляр MySerial как атрибут class , то он будет доступен втело класса:

class App():
    ser = MySerial()

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

Декорирование при каждом создании

Или, если вам нужен отдельный экземпляр MySerial для каждого экземпляра App, вам нужно будет подождать, пока экземпляр будетсоздан для определения атрибута instance my_func.Это означает, что функция динамически декорируется при каждом создании экземпляра, и в этом случае синтаксис декоратора @ должен быть заменен вызовом функции.

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))
0 голосов
/ 01 июня 2018

Есть много скрытых ловушек, которые делают этот рискованный дизайн, однако, это отличный пример обучения.

Во-первых, вызов '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 необходимо для получения уникального пространства имен для экземпляра.Именно поэтому вы не сможете получить декоратор, который является уникальным для экземпляра таким образом.Еще один способ подумать о том, что класс должен быть определен до того, как вы сможете создавать экземпляры.Следовательно, вы не можете использовать экземпляр в определении класса.

Решение

Декоратор должен быть написан так, чтобы не хранить никаких атрибутов экземпляра.Вместо этого он получит доступ к атрибутам экземпляра приложения.

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 будет уникальный файл, или порт, или что-то еще для каждого экземпляра приложения.Это то, что будет записано в serial_config.Это может быть не элегантно, если поток открывает закрытие, но вы сможете улучшить его для своего конкретного приложения.

0 голосов
/ 01 июня 2018

Вы можете использовать staticmethod, чтобы обернуть decorator.Внутренняя func_wrap функция decorator содержит дополнительный параметр в своей подписи: cls.cls может использоваться для доступа к атрибуту ser экземпляра App, а затем из 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()
...