Python:通过lambda包装函数

时间:2012-02-19 09:10:35

标签: python wrapper decorator lambda

我有一些像

这样的代码
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 个答案:

答案 0 :(得分:7)

它以无限递归结束,因为调用lambda e: log_event(handler.handle, e)时,handler.handle已经是 lambda 表达式。 log_event会调用 lambda lambda 会调用log_event等等。

要解决此问题,只需将当前方法保存在本地范围内,这也不需要额外的lambda表达式。

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:\用户\尼古拉斯\桌面和GT; foo.py
  调用句柄:( EventHandler实例,'event'){}

答案 1 :(得分:2)

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

匿名函数将在调用时查找handler的{​​{1}}成员并将其(以及handle)传递给e。因为您立即将log_event设置为匿名函数,所以匿名函数只会获取对自身的引用。

另一方面:

handler.handle

获取handle_func = handler.handle handler.handle = lambda e: log_event(handle_func, e) 的方法一次(具体来说,你得到一个“绑定方法”对象粘合对象和底层函数对象),然后你才创建匿名函数并覆盖{{1} }。

答案 2 :(得分:1)

因为函数是对象,所以不需要使用lambda将它们分配给变量。而是做:

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

在该代码中,您避免在log_event中评估handler.handle,因此不会发生递归。

使用装饰器会更常见,但装饰器会在内部做同样的事情。