How can I get the argument spec on a decorated function?

前端 未结 2 1609
日久生厌
日久生厌 2020-12-29 05:17

I need to determine the argspec (inspect.getargspec) of a function within a decorator:

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


        
相关标签:
2条回答
  • 2020-12-29 05:46

    I've written a simple class that does what you want. This will achieve the same thing that functools.wraps does as well as preserve the function's signature (from getargspec's point of view). Read the docstring for this class on my gist for more information.

    Note: this only works on decorating functions and not on class methods.

    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
    

    Simply create a new decorator by subclassing

    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)
    

    Use it like any normal decorated function

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

    Output

    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.
    
    0 讨论(0)
  • 2020-12-29 06:07

    If you use Michele Simionato's decorator module to decorate your function, its decorator.decorator will preserve the original function's signature.

    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))
    
    0 讨论(0)
提交回复
热议问题