Украшение метода - PullRequest
       14

Украшение метода

4 голосов
/ 04 августа 2010

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

Мне бы хотелось, чтобы это выглядело так:сначала попытался сделать это так:

def listento(to):
    def listen_(func):
        myEventManager.listen(to, func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return func
    return listen_

Когда я вызываю myEventManger.listen('event', self.method) изнутри экземпляра, все работает нормально.Однако, если я использую подход декоратора, аргумент self никогда не передается.

Другой подход, который я попробовал после поиска решения в Интернете, заключается в использовании класса в качестве декоратора:

class listen(object):
    def __init__(self, method):
        myEventManager.listen('frontend.route.register', self)
        self._method = method
        self._name = method.__name__
        self._self = None

    def __get__(self, instance, owner):
        self._self = instance
        return self

    def __call__(self, *args, **kwargs):
        return self._method(self._self, *args, **kwargs)

Проблема этого подхода в том, что я не совсем понимаю концепцию __get__ и не знаю, как бы я включил параметры.Просто для тестирования я пытался использовать фиксированное событие для прослушивания, но с этим подходом ничего не происходит.Когда я добавляю операторы печати, я вижу, что __init__ вызывается.Если я добавлю дополнительную регистрацию событий в «старом стиле», то и __get__, и __call__ будут выполнены, и событие сработает, несмотря на новый декоратор.ищу, или я просто упускаю какую-то важную концепцию с декораторами?

Ответы [ 2 ]

4 голосов
/ 04 августа 2010

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

class Foo(object):
  @some_decorator
  def bar(self, *args, **kwargs):
    # etc etc

, тогда при создании класса Foo будет вызываться some_decorator, и ему будет передан несвязанный метод, а не связанный метод экземпляра.Вот почему self не передается.

Второй метод, с другой стороны, может работать до тех пор, пока вы создаете только один объект из каждого класса, который используете декоратор. и , если вы немного сообразительны.Если вы определите listen, как указано выше, а затем определите

class Foo(object):
  def __init__(self, *args, **kwargs):
    self.some_method = self.some_method # SEE BELOW FOR EXPLANATION
    # etc etc
  @listen
  def some_method(self, *args, **kwargs):
    # etc etc

Тогда listen.__get__ будет вызван, когда кто-то попытается вызвать f.some_method напрямую для некоторого f ... но весь смысл вашегоСуть в том, что никто этого не делает!Механизм обратного вызова события вызывает непосредственный вызов экземпляра listen, потому что это то, что он получает, и экземпляр listen вызывает несвязанный метод, который он сжимал при создании.listen.__get__ никогда не будет вызван, а параметр _self никогда не будет установлен должным образом ... , если только вы сами явно не обращаетесь к self.some_method, как я делал в методе __init__ выше.Тогда listen.__get__ будет вызываться при создании экземпляра и _self будет установлен правильно.

Проблема в том, что (а) это ужасный, ужасный взлом и (б) если вы попытаетесь создать два экземпляра Foo, тогда второй перезапишет _self, установленный первым, потому что все еще создается только один listen объект, который связан с классом, а не с экземпляром.Если вы когда-либо используете только один экземпляр Foo, то все в порядке, но если вам нужно, чтобы событие вызывало два разных Foo, вам просто нужно будет использовать регистрацию вашего события "старого стиля".

Версия TL, DR: декорирование метода украшает несвязанный метод класса, тогда как вы хотите, чтобы ваш менеджер событий получил метод bound экземпляра.

3 голосов
/ 04 августа 2010

Часть вашего кода:

    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return func

, который определяет wrapper, затем полностью игнорирует его и возвращает func. Трудно сказать, является ли это реальной проблемой в вашем реальном коде, потому что, очевидно, вы не публикуете это (как доказано опечатками, такими как myEventManagre, myEvnetManager и т. Д.), Но если вы это делаете в своей реальной код это очевидно, часть вашей проблемы.

...