Python: обернуть функции через лямбду - PullRequest
0 голосов
/ 19 февраля 2012

У меня есть код вроде

class EventHandler:
    def handle(self, event):
        pass

def wrap_handler(handler):
    def log_event(proc, e):
        print e
        proc(e)
    handler.handle = lambda e: log_event(handler.handle, e)

handler = EventHandler()
wrap_handler(handler)
handler.handle('event')

, что закончится бесконечной рекурсией. При изменении wrap_handler на

def wrap_handler(handler):
    def log_event(proc, e):
        print e
        proc(e)
    # handler.handle = lambda e: log_event(handler.handle, e)
    handle_func = handler.handle
    handler.handle = lambda e: log_event(handle_func, e)

программа станет ОК. Это почему? И кто-нибудь может сказать мне более распространенные способы обернуть функции?

Ответы [ 3 ]

7 голосов
/ 19 февраля 2012

Это заканчивается бесконечной рекурсией, например, когда вызывается lambda e: log_event(handler.handle, e), handler.handle уже является выражением лямбда . log_event будет вызывать лямбда , а лямбда будет вызывать log_event и т. Д.

Чтобы исправить это, просто сохраните текущий метод в локальной области, для этого также не требуется дополнительное лямбда-выражение.

class EventHandler:
    def handle(self, event):
        pass

def wrap_handler(handler):
    proc = handler.handle
    def log_event(e):
        print e
        proc(e)
    handler.handle = log_event

handler = EventHandler()
wrap_handler(handler)
handler.handle('event')

Вы также можете использовать декоратор.

def logging(function):
    def wrapper(*args, **kwargs):
        print "Calling %s with:" % function.__name__, args, kwargs
        return function(*args, **kwargs)
    return wrapper

class EventHandler:
    @ logging
    def handle(self, event):
        pass

    def __repr__(self):
        return "EventHandler instance"

handler = EventHandler()
handler.handle('event')

C: \ Users \ Никлас \ Desktop> foo.py
Дескриптор вызова с: (экземпляр EventHandler, 'событие') {}

2 голосов
/ 19 февраля 2012
handler.handle = lambda e: log_event(handler.handle, e)

Анонимная функция после вызова ищет handler член handle и передает его (вместе с e) в log_event. Поскольку вы немедленно устанавливаете handler.handle для анонимной функции, анонимная функция просто получает ссылку на себя.

Это с другой стороны:

handle_func = handler.handle
handler.handle = lambda e: log_event(handle_func, e)

Получает метод handler один раз (в частности, вы получаете объект «связанный метод», склеивающий объект и объект базовой функции вместе), и только затем вы создаете анонимную функцию и перезаписываете handler.handle.

1 голос
/ 19 февраля 2012

Поскольку функции являются объектами, вам не нужно использовать лямбду для присвоения их переменной.Вместо этого выполните:

def wrap_handler(handler):
    proc = handler.handle
    def log_event(e):
        print e
        proc(e)
    # handler.handle = lambda e: log_event(handler.handle, e)
    handler.handle = log_event

В этом коде вы избегаете оценки handler.handle в log_event, поэтому рекурсия не происходит.

Чаще всего использовать декораторы, но декораторы будут делатьпочти то же самое внутри.

...