【问题标题】:How can I get the argument spec on a decorated function?如何获取装饰函数的参数规范?
【发布时间】:2010-10-19 20:09:03
【问题描述】:

我需要确定装饰器中函数的 argspec (inspect.getargspec):

def decor(func):
    @wraps(func)
    def _decor(*args, **kwargs):
        return func(*args, **kwargs)
    return _decor

@decor
def my_func(key=1, value=False):
    pass

我需要能够检查包装的“my_func”并返回键/值参数及其默认值。看来 inspect.getargspec 没有得到正确的功能。

(FWIW 我需要这个来进行一些运行时检查/验证和以后的文档生成)

【问题讨论】:

    标签: python


    【解决方案1】:

    如果您使用 Michele Simionato 的decorator module 来装饰您的功能, 它的decorator.decorator 将保留原始函数的签名。

    import inspect
    import decorator
    
    @decorator.decorator
    def decor(my_func,*args,**kw):
        result=my_func(*args,**kw)
        return result
    
    @decor
    def my_func(key=1, value=False):
        pass
    decorated_argspec = inspect.getargspec(my_func)
    print(decorated_argspec)
    # ArgSpec(args=['key', 'value'], varargs=None, keywords=None, defaults=(1, False))
    

    【讨论】:

    • 为什么结果中的keywords=None?
    • 名称有点混乱。 keywords 设置为 ** 参数的名称。例如,如果def bar(**kw): pass,则inspect.getargspec(bar) 将返回ArgSpec(args=[], varargs=None, keywords='kw', defaults=None)
    【解决方案2】:

    我已经写了一个简单的类来做你想做的事。这将实现与functools.wraps 相同的功能,并保留函数的签名(从getargspec 的角度来看)。在 my gist for more information 上阅读此类的文档字符串。

    注意:这仅适用于装饰函数,不适用于类方法。

    import types
    
    class decorator(object):
        def __getattribute__(self, name):
            if name == '__class__':
                # calling type(decorator()) will return <type 'function'>
                # this is used to trick the inspect module >:)
                return types.FunctionType
            return super(decorator, self).__getattribute__(name)
    
        def __init__(self, fn):
            # let's pretend for just a second that this class
            # is actually a function. Explicity copying the attributes
            # allows for stacked decorators.
            self.__call__ = fn.__call__
            self.__closure__ = fn.__closure__
            self.__code__ = fn.__code__
            self.__doc__ = fn.__doc__
            self.__name__ = fn.__name__
            self.__defaults__ = fn.__defaults__
            self.func_defaults = fn.func_defaults
            self.func_closure = fn.func_closure
            self.func_code = fn.func_code
            self.func_dict = fn.func_dict
            self.func_doc = fn.func_doc
            self.func_globals = fn.func_globals
            self.func_name = fn.func_name
            # any attributes that need to be added should be added
            # *after* converting the class to a function
            self.args = None
            self.kwargs = None
            self.result = None
            self.function = fn
    
        def __call__(self, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs
    
            self.before_call()
            self.result = self.function(*args, **kwargs)
            self.after_call()
    
            return self.result
    
        def before_call(self):
            pass
    
        def after_call(self):
            pass
    

    通过子类化创建一个新的装饰器

    import time
    
    class timeit(decorator):
        def before_call(self):
            self.start = time.time()
        def after_call(self):
            end = time.time()
            print "Function {0} took {1} seconds to complete.".format(
                self.__name__, end - self.start
            )
    
    @timeit
    def my_really_cool_function(a, b, c, d='asdf', q='werty'):
        time.sleep(5)
    

    像任何普通的装饰函数一样使用它

    args = inspect.getargspec(my_really_cool_function)
    print args
    
    my_really_cool_function(1,2,3,4,5)
    

    输出

    ArgSpec(args=['a', 'b', 'c', 'd', 'q'], varargs=None,
            keywords=None, defaults=('asdf', 'werty'))
    Function my_really_cool_function took 5.0 seconds to complete.
    

    【讨论】:

      猜你喜欢
      • 2013-06-03
      • 2018-01-21
      • 1970-01-01
      • 2013-07-04
      • 2021-11-18
      • 2019-07-29
      • 2010-11-03
      • 1970-01-01
      • 2019-09-24
      相关资源
      最近更新 更多