How can I programmatically change the argspec of a

2019-01-15 18:20发布

问题:

Given a function:

def func(f1, kw='default'):
    pass
bare_argspec = inspect.getargspec(func)

@decorator
def func2(f1, kw='default'):
    pass
decorated_argspec = inspect.getargspec(func2)

How can I create a decorator such that bare_argspec == decorated_argspec?

(As to why, the framework that calls the decorated function does argspec inspection to choose what to pass in, so the decorator has to retain the same argspec in order to play nice. When I posed this question on #python, I got a long speech about why the framework sucks, which is not what I'm looking for; I have to solve the problem here. Also, I'm just interested in the answer, too)

回答1:

Michele Simionato's decorator module has a decorator called decorator which preserves function argspecs.

import inspect
import decorator

def func(f1, kw='default'):
    pass
bare_argspec = inspect.getargspec(func)
print(bare_argspec)
# ArgSpec(args=['f1', 'kw'], varargs=None, keywords=None, defaults=('default',))

@decorator.decorator
def mydecorator(func,*args,**kw):
    result=func(*args,**kw)
    return result

@mydecorator
def func2(f1, kw='default'):
    pass
decorated_argspec = inspect.getargspec(func2)
print(decorated_argspec)
# ArgSpec(args=['f1', 'kw'], varargs=None, keywords=None, defaults=('default',))

assert(bare_argspec==decorated_argspec)


回答2:

There's the decorator module:

from decorator import decorator
@decorator
def trace(func, *args, **kw):
    print 'calling', func, 'with', args, kw
    return func(*args, **kw)

That makes trace a decorator with the same argspecs as the decorated function. Example:

>>> @trace
... def f(x, y=1, z=2, *args, **kw):
...     pass

>>> f(0, 3)
calling f with (0, 3, 2), {}

>>> from inspect import getargspec
>>> print getargspec(f)
ArgSpec(args=['x', 'y', 'z'], varargs='args', keywords='kw', defaults=(1, 2))


回答3:

Are functools.update_wrapper() and/or functools.wraps() good enough?