我有一个带args和kwargs的函数,我需要在我的装饰器中根据函数中 2nd arg的值做一些事情,如下面的代码所示: / p>
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允许用户使用第二个参数作为常规参数或关键字参数调用该函数,因此如果用户使用my_func
作为kwarg调用arg2
,则会引发IndexError
,见下文:
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
有没有办法解决这个问题而不仅仅是try/except
并抓住IndexError
?
答案 0 :(得分:5)
这是我能想到的最强大的方法来处理它......诀窍是检查第二个参数的名称。然后,在装饰器中,检查kwargs
中是否存在该名称。如果是,那么你使用它。如果不是,则使用args
。
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)
运行此结果:
foo
foo
no foo for you
no foo for you
正如所料。
答案 1 :(得分:4)
我使用python decorator
包找到了答案。该软件包的一个特性是无论用户如何传递它,它都会保留位置/关键字args。它具有减少大量代码的额外好处,因此我的原始代码:
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))
变为:
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))
答案 2 :(得分:-1)
似乎arg1是必需参数,但arg2可能是可选的。您可以将第3行更改为:
def case_decorator(arg1, arg2=None, *args, **kwargs):
# or =0 or ='' whichever best
和第6行:
return fn(arg1, arg2, *args, **kwargs)
如果arg1也是可选的,请使用arg1 = None或= 0或=''
更改第3行