Как передать себя в экземпляр функции, когда она назначается в декораторе? - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь назначить словарные ключи объектным функциям, но по какой-то причине он не будет работать внутри декораторов. Когда я пытаюсь вызвать a.run (), self, кажется, не передается в словарь fun c. У меня также нет доступа к f. self в декораторе, поэтому я знаю, что там должно быть что-то не так. Я написал простой пример моего кода. Я хочу, чтобы он был чем-то похожим на app.route в flask, поскольку он инициирует сопоставление между конечными точками и функциями.

ОШИБКА:

    Traceback (most recent call last):
  File "main.py", line 27, in <module>
    a.run()
  File "main.py", line 14, in run
    self.rmap[k](data)
TypeError: one_way() missing 1 required positional argument: 'data'

КОД:

class A (object):
  def __init__(self):
    self.rmap = {}

  def route(self, r):
    def decorator(f):
      self.rmap[r] = f
      return f
    return decorator

  def run(self):
    data = [1,2,3]
    for k in self.rmap.keys():
      self.rmap[k](data)

a = A()

class B (object):
  def __init__(self):
    pass


  @a.route('/one/way')
  def one_way (self, data):
    print('A WAY:{}'.format(self))

b = B()
a.run()

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

Во время оформления one_way() представляет собой простую функцию , а не метод - она ​​становится методом только при поиске на экземпляре B. Итак, вам нужно явно предоставить экземпляр B при вызове его из A().run() (тот факт, что в вашем коде есть глобальный экземпляр b, не имеет значения - объект функции, хранящийся в a.rmap, абсолютно ничего не знает об этом, и даже не о B классе FWIW.

Короче говоря, ваш текущий дизайн не может работать как есть. Если вы когда-нибудь намереваетесь декорировать методы (ну, функции) из одного класса и вызывать их в одном экземпляре этого класса, вы можете передать экземпляр этого класса в a.run() ie:

class A():
   # ...
   def run(self, obj):
    data = [1,2,3]
    for k in self.rmap.keys():
        self.rmap[k](obj, data)

b = B()
a.run(b)

, но это будет очень ограниченное использование.

Или Вы можете просто использовать декоратор, чтобы «пометить» функции, которые будут использоваться для маршрутизации (вместе с эффективным маршрутом), добавить немного register() methdo к A и явно передать B или любой другой экземпляр этого метода ie

 def route(r):
    def decorator(f):
        f._A_route = r
        return f
    return decorator


class A (object):
    def __init__(self):
        self.rmap = {}

    def register(self, *objects):
        for obj in objects:
            self._register(obj)

    def _register(self, obj):
        for name in dir(obj):
            if name.startswith("_"):
                continue
            attr = getattr(obj, name)
            if callable(attr) and hasattr(attr, "_A_route"):
                self.rmap[attr._A_route] = attr

    def run(self):
        data = [1,2,3]
        for k in self.rmap.keys():
            self.rmap[k](data)

class B (object):
    def __init__(self):
        pass


    @route('/one/way')
    def one_way (self, data):
        print('A WAY:{}'.format(self))

if __name__ == "__main__":
    a = A()
    b = B()
    a.register(b)
    a.run()

Теперь могут быть лучшие решения для вашего конкретного варианта использования, но невозможно сказать, не зная всего контекста и т. Д. c. * 1 024 *

1 голос
/ 07 апреля 2020

При вызове self.rmap[k](data) вы не передаете параметр self. Это должен быть экземпляр класса B, чтобы работать.

Обычно вы просто передаете параметры, с которыми была вызвана декорированная функция, но вы, похоже, хотите использовать декорированную функцию по-другому , В вашем случае будет работать:

def run(self):
    data = [1,2,3]
    b = B()
    for k in self.rmap.keys():
        self.rmap[k](b, data)

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

...