Python:在循环中包装函数

时间:2014-08-04 16:33:41

标签: python function wrapper decorator

在Python中循环中包含多个函数的正确方法是什么? 我有通用包装器和功能列表。我需要在一个循环中包含列表中的所有函数,但for f in funcs:对我不起作用。 执行此操作的正确方法是什么?

def orig_func1(x, y):
    print "x=%i y=%i" % (x,y)

def orig_func2(a, b):
    print "a=%i b=%i" % (a,b)

def wrapper(func):
    f_name = func.__name__
    print 'adding hook for function [%s]' % f_name
    def inner(*args, **kwargs):
        print 'I am before original function'
        ret = func(*args, **kwargs)
        print 'I am after original function'
        return ret
    return inner

funcs = [orig_func1, orig_func2]
print funcs

for f in funcs:
    f =  wrapper(f)

print funcs

,结果显示列表中的函数未更改:

[<function orig_func1 at 0x022F78F0>, <function orig_func2 at 0x022F7930>]
adding hook for function [orig_func1]
adding hook for function [orig_func2]
[<function orig_func1 at 0x022F78F0>, <function orig_func2 at 0x022F7930>]
x=1 y=2
a=3 b=4

3 个答案:

答案 0 :(得分:1)

在该循环中,f只是一个局部变量。除非直接修改列表,否则您不会更改任何有意义的内容。而不是:

for f in funcs:
    f =  wrapper(f)

你应该这样做:

for i, f in enumerate(funcs):
    funcs[i] = wrapper(f)

这会将列表中的功能更改为可以使用的新功能。但它仍然不会改变功能的最终定义。一旦定义完毕,没有什么能够完成重新定义或在函数定义之上使用的包装器;直接调用orig_func1将在for循环之前和之后净化相同的结果。如果要在运行时修改函数,则必须继续引用您刚刚创建的函数的这个包装版本。

答案 1 :(得分:1)

您应该使用Python Decorators而不是遍历尝试包装它们的函数。它们是修改函数行为的正确方法,而不是当前的循环方法。如果官方文档没有说清楚,herehere是一些帮助我很多的教程。

您现有的代码实际上看起来很像我的第一个教程链接中的一些代码片段。您应该使用@decorator语法而不是手动换行来替换循环。

话虽如此,你可以通过理解来完成你原本想要的东西。用这个替换你的循环:

funcs = [wrapper(func) for func in funcs]

其他评论和回答是正确的,你在循环中修改 f 是行不通的,因为它有一个本地的范围并且没有修改你的列表。

答案 2 :(得分:0)

我使用以下方法实现了所需的行为:

import Module1, Module2

funcs = ['Module1.func1','Module1.func2','Module2.func1','Module2.func2']
hooks = {}

def wrapper(func,f_name):
    if not hooks.has_key(f_name):
        hooks[f_name] = {'before':[],
                         'after':[]}
    def inner(*args, **kwargs):
        for f in hooks[f_name]['before']:
            f(*args, **kwargs)
        ret = func(*args, **kwargs)
        for f in hooks[f_name]['after']:
            f(*args, **kwargs)
        return ret
    return inner

def implementHooks():
    for f in funcs:
        obj_name, func_name = f.rsplit('.',1)
        obj = globals()[obj_name]
        func = getattr(obj, func_name)
        setattr(obj, func_name, wrapper(func, f))

implementHooks()

def module1_func1_hook():
    print 'Before running module1.func1()'

hooks['Module1.func1']['before'] += [module1_func1_hook]