【问题标题】:How to get through decorator to get the underlying function arguments information?如何通过装饰器获取底层函数参数信息?
【发布时间】:2013-07-04 04:40:42
【问题描述】:

我将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,因为 bar 由装饰器 @showduration 装饰。我的问题是如何穿透装饰器获取底层函数的信息(参数列表和默认值)?

感谢您的提示。

【问题讨论】:

标签: python arguments decorator


【解决方案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)

【讨论】:

  • 不错的提示。我从您的解决方案中学习了如何从底层函数传递信息的方法。谢谢。
【解决方案2】:

假设您已经安装了 Michele Simionato 的 decorator 模块,您可以通过对它和其中定义的嵌套wrapped_f()函数进行一些小的修改来使您的showdurationdecorator 与它一起工作,以便后者适合签名该模块的 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)  

【讨论】:

  • 感谢您提供有关如何使用 decorator.decorator() 的详细解决方案,非常有帮助。我接受@martineau 的回答,因为我更喜欢不太依赖第三个包的解决方案。无论如何,谢谢。
  • P.S.顺便说一句,你不应该过分担心使用 Michele Simionato 的 decorator 模块。它是纯 Python,易于安装,文档齐全,看起来非常健壮,可与 Python 2 和 3 一起使用。
  • 很抱歉,我的粗心无法识别两个名称相同。 :-)
猜你喜欢
  • 2019-09-24
  • 1970-01-01
  • 2018-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 2019-07-29
相关资源
最近更新 更多