如何通过装饰器获取底层函数参数信息?

时间:2013-07-06 04:37:47

标签: python arguments decorator

我按下面的代码编写get_function_arg_data(func)来获取函数func的参数信息:

def get_function_arg_data(func):
    import inspect
    func_data = inspect.getargspec(func)
    args_name = func_data.args #func argument list
    args_default = func_data.defaults #funcargument default data list
    return args_name, args_default


def showduration(user_function):
    ''' show time duration decorator'''
    import time
    def wrapped_f(*args, **kwargs):
        t1 = time.clock()
        result = user_function(*args, **kwargs)
        print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
        return result
    return wrapped_f


def foo(para1, para2=5, para3=7):
    for i in range(1000):
        s = para1+para2+para3
    return s

@showduration
def bar(para1, para2, para3):
    for i in range(1000):
        s=para1+para2+para3
    return s

print get_function_arg_data(foo)
bar(1,2,3)
print get_function_arg_data(bar)


>>> 
(['para1', 'para2', 'para3'], (5, 7))
bar()_Time: 0.00012
([], None)
>>> 

get_function_arg_data()适用于foo,不适用于bar,因为条形图由装饰器@showduration修饰。我的问题是如何穿透装饰器来获取底层函数的信息(参数列表和默认值)?

感谢您的提示。

2 个答案:

答案 0 :(得分:1)

假设您已经安装了Michele Simionato的decorator模块,您可以对showduration装饰器进行一些微小的修改,并对其中定义的嵌套wrapped_f()函数进行修改。所以后者符合模块的decorator.decorator()函数所期望的签名:

import decorator

def showduration(user_function):
    ''' show time duration decorator'''
    import time
    def wrapped_f(user_function, *args, **kwargs):
        t1 = time.clock()
        result = user_function(*args, **kwargs)
        print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
        return result
    return decorator.decorator(wrapped_f, user_function)

然而,该模块真的很闪耀,因为它可以让你将上面的样板材减少到只有:

import decorator

@decorator.decorator
def showduration(user_function, *args, **kwargs):
    import time
    t1 = time.clock()
    result = user_function(*args, **kwargs)
    print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
    return result

使用上述任一组更改,您的示例代码将输出:

(['para1', 'para2', 'para3'], (5, 7))
bar()_Time: 0.00026                  
(['para1', 'para2', 'para3'], None)  

答案 1 :(得分:1)

我认为没有,或者至少知道任何“渗透”装饰函数和获取基础函数信息的一般方法,因为Python的函数修饰概念是所以一般 - 如果事实上,一般来说,没有任何东西需要或保证原始函数将被调用(尽管通常是这种情况)。

因此,一个更实际的问题是:我如何编写自己的装饰器,这将允许我检查底层函数的参数信息?

之前建议的一个简单方法是使用Michele Simionato的decorator模块(以及与之兼容的写装饰器)。

执行此操作的不太强大但非常简单的方法是根据您问题中的代码执行下面显示的操作:

def get_function_arg_data(func):
    import inspect
    func = getattr(func, '_original_f', func) # use saved original if decorated
    func_data = inspect.getargspec(func)
    args_name = func_data.args #func argument list
    args_default = func_data.defaults #funcargument default data list
    return args_name, args_default

def showduration(user_function):
    '''show time duration decorator'''
    import time
    def wrapped_f(*args, **kwargs):
        t1 = time.clock()
        result = user_function(*args, **kwargs)
        print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
        return result
    wrapped_f._original_f = user_function  # save original function
    return wrapped_f

def foo(para1, para2=5, para3=7):
    for i in range(1000):
        s = para1+para2+para3
    return s

@showduration
def bar(para1, para2, para3):
    for i in range(1000):
        s=para1+para2+para3
    return s

print 'get_function_arg_data(foo):', get_function_arg_data(foo)
print 'get_function_arg_data(bar):', get_function_arg_data(bar)

所有修改都涉及将原始函数保存在名为_original_f的属性中,该属性添加了装饰器返回的包装函数。然后get_function_arg_data()函数只检查此属性并根据其值返回信息,而不是传递给它的修饰函数。

虽然这种方法不适用于任何修饰函数,但只有那些已经添加了特殊属性的函数,它与Python 2& 3。

显示的代码产生的输出:

get_function_arg_data(foo): (['para1', 'para2', 'para3'], (5, 7))
get_function_arg_data(bar): (['para1', 'para2', 'para3'], None)