1

我编写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. 我的问题是如何穿透装饰器来获取底层函数的信息(参数列表和默认值)?

感谢您的提示。

4

2 回答 2

1

我认为没有,或者至少不知道,有任何通用的方法可以“穿透”一个装饰函数并获取底层函数的信息,因为 Python 的函数装饰概念是如此笼统——如果事实上,一般来说,什么都没有这要求或保证将完全调用原始函数(尽管通常是这种情况)。

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

之前建议的一种简单方法是使用 Michele Simionato 的装饰器模块(并编写与其兼容的装饰器)。

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

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)
于 2013-07-07T17:39:38.120 回答
1

假设您已经安装了 Michele Simionato 的装饰器模块,您可以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)  
于 2013-07-06T13:38:24.243 回答