2012-02-19 88 views
0

包装功能我有一些这样的代码的Python:通过拉姆达

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) 

程序将变为OK。这是为什么?谁能告诉我更常用的方法来包装功能?

+2

通常你使用装饰器包装函数... – Gandaro 2012-02-19 09:17:35

回答

7

它在无限递归中结束,因为当调用lambda e: log_event(handler.handle, e)时,handler.handle已经是lambda表达式。 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:\用户\尼古拉斯\桌面> foo.py
调用句柄与:(事件处理程序的实例, '事件'){}

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,所以没有发生递归。

使用装饰器是比较平常的事情,但装饰器在内部会做很多相同的事情。

+1

这将导致一个* NameError *,因为没有名为'e'的变量可用。此外,这将执行该函数并分配它的返回值,并且它不会分配整个函数。 – 2012-02-19 09:27:18

+0

并没有'proc'(在'log_event'内部调用)。 – yak 2012-02-19 12:15:27

+0

@ yak:是的。 – Marcin 2012-02-19 13:00:00

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

匿名功能将,在调用后,查找handlerhandle成员,并将其(与e一起)传递给log_event。因为您立即将handler.handle设置为匿名函数,匿名函数仅获取对自身的引用。

这在另一方面:

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

获取handler“s法一次(具体而言,会得到一个‘结合方法’对象胶粘对象和底层函数对象一起),才把你创建匿名功能并覆盖handler.handle