Parsing args and kwargs in decorators
我有一个接受args和kwargs的函数,我需要根据函数中第二个arg的值在decorator中执行一些操作,如下代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 | def workaround_func(): def decorator(fn): def case_decorator(*args, **kwargs): if args[1] == 2: print('The second argument is a 2!') return fn(*args, **kwargs) return case_decorator return decorator @workaround_func() def my_func(arg1, arg2, kwarg1=None): print('arg1: {} arg2: {}, kwargs: {}'.format(arg1, arg2, kwarg1)) |
问题在于,python允许用户使用第二个参数作为常规参数或关键字参数来调用函数,因此,如果用户使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | In [8]: d.my_func(1, 2, kwarg1=3) The second argument is a 2! arg1: 1 arg2: 2, kwargs: 3 In [9]: d.my_func(1, arg2=2, kwarg1=3) --------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-9-87dc89222a9e> in <module>() ----> 1 d.my_func(1, arg2=2, kwarg1=3) /home/camsparr/decoratorargs.py in case_decorator(*args, **kwargs) 2 def decorator(fn): 3 def case_decorator(*args, **kwargs): ----> 4 if args[1] == 2: 5 print('The second argument is a 2!') 6 return fn(*args, **kwargs) IndexError: tuple index out of range |
号
有没有一种方法可以解决这个问题,而不只是做一个
这是我能想到的处理它的最有力的方法…技巧是检查第二个参数的名称。然后,在decorator中,检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from inspect import getargspec def decorate(fn): argspec = getargspec(fn) second_argname = argspec[0][1] def inner(*args, **kwargs): special_value = (kwargs[second_argname] if second_argname in kwargs else args[1]) if special_value == 2: print"foo" else: print"no foo for you" return fn(*args, **kwargs) return inner @decorate def foo(a, b, c=3): pass foo(1,2,3) foo(1,b=2,c=4) foo(1,3,5) foo(1,b=6,c=5) |
运行此命令将导致:
1 2 3 4 | foo foo no foo for you no foo for you |
果不其然。
我使用python
1 2 3 4 5 6 7 8 9 10 11 12 | def workaround_func(): def decorator(fn): def case_decorator(*args, **kwargs): if args[1] == 2: print('The second argument is a 2!') return fn(*args, **kwargs) return case_decorator return decorator @workaround_func() def my_func(arg1, arg2, kwarg1=None): print('arg1: {} arg2: {}, kwargs: {}'.format(arg1, arg2, kwarg1)) |
变成:
1 2 3 4 5 6 7 8 9 10 11 | from decorator import decorator @decorator def workaround_decorator(f, *args, **kwargs): if args[1] == 2: print('The second argument is 2!') return f(*args, **kwargs) @workaround_decorator def my_func(arg1, arg2, kwarg1=None): print('arg1: {} arg2: {}, kwargs: {}'.format(arg1, arg2, kwarg1)) |
arg1似乎是必需的参数,但arg2可能是可选的。您可以将第3行更改为:
1 2 | def case_decorator(arg1, arg2=None, *args, **kwargs): # or =0 or ='' whichever best |
第6行:
1 | return fn(arg1, arg2, *args, **kwargs) |
如果arg1也是可选的,请将第3行更改为arg1=none或=0或='