python 参数的默认值是否被 args 或 kwargs 接受?
Is python parameters' default value accepted by args or kwargs?
我在尝试制作测试装饰器时发现了一个奇怪的实例。代码如下:
def doublefunc(fn):
def warpped(*arg, **kwargs):
arg = (i if type(i) is not int else i*2 for i in arg)
for k, v in kwargs.iteritems():
kwargs[k] = v *2 if type(v) is int else v
return fn(*arg, **kwargs)
return warpped
@doublefunc
def add(x, y, m, z=10, v='bbaa'):
print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v)
return x + y + z
当我这样调用函数时:
print add(1, 1, 'teststring')
返回值是14而不是24,也就是说函数add中参数z的默认值,在装饰函数内部不被接受。为什么?
这很棘手,但您可以通过使用 inspect
模块
获得未传递的默认参数
import inspect
def get_unpassed_defaults(fn, args, kwargs):
args_len = len(args) + len(kwargs)
argspec = inspect.getargspec(fn)
defaults = argspec.defaults
default_names = argspec.args[-len(defaults):]
number_of_args = len(argspec.args)
number_of_positional_args = number_of_args - len(argspec.defaults)
number_of_kwargs = number_of_args - number_of_positional_args
number_of_kwargs_passed_as_positional = len(args) - number_of_positional_args
default_values = defaults[number_of_kwargs_passed_as_positional:]
default_names = default_names[number_of_kwargs_passed_as_positional:]
defaults_dict = dict(zip(default_names, default_values))
for kwarg_name in kwargs:
if kwarg_name in defaults_dict:
defaults_dict.pop(kwarg_name)
return defaults_dict
然后在 doublefunc
中利用它:
def doublefunc(fn):
def wrapped(*arg, **kwargs):
unpassed_defaults = get_unpassed_defaults(fn, arg, kwargs)
kwargs.update(unpassed_defaults)
arg = (i if type(i) is not int else i*2 for i in arg)
for k, v in kwargs.iteritems():
kwargs[k] = v *2 if type(v) is int else v
return fn(*arg, **kwargs)
return wrapped
@doublefunc
def add(x, y, m, z=10, v='bbaa'):
print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v)
return x + y + z
print add(1, 1, "teststring", v=3)
给出:
x=2, y=2, m=teststring, z=20, v=6
24
原因是装饰器函数不查找包装函数的内部属性(在本例中为默认 kwargs)。如果您仍然想这样做,那么您只需要使用字典即可。
Won't work with Python 2 because there is no getfullargspec
method, so for that follow @Nolen Royalty's answer
为Python3
>>> import inspect
>>> def doublefunc(fn):
def wrapped(*arg, **kwargs):
defaults = (inspect.getfullargspec(fn).kwonlydefaults)
defaults.update(kwargs)
kwargs = defaults
arg = (i if type(i) is not int else i*2 for i in arg)
for k, v in kwargs.items():
kwargs[k] = v *2 if type(v) is int else v
return fn(*arg, **kwargs)
return wrapped
>>> @doublefunc
def add(x, y, m, *args, z=10, v='bbaa'):
print('x={0}, y={1}, m= {2}, z={3}, v={4}'.format(x,y,m,z,v))
return x + y + z
>>> print(add(1, 1, 'teststring', v=1))
结果:
x=2, y=2, m= teststring, z=20, v=2
24
我在尝试制作测试装饰器时发现了一个奇怪的实例。代码如下:
def doublefunc(fn):
def warpped(*arg, **kwargs):
arg = (i if type(i) is not int else i*2 for i in arg)
for k, v in kwargs.iteritems():
kwargs[k] = v *2 if type(v) is int else v
return fn(*arg, **kwargs)
return warpped
@doublefunc
def add(x, y, m, z=10, v='bbaa'):
print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v)
return x + y + z
当我这样调用函数时:
print add(1, 1, 'teststring')
返回值是14而不是24,也就是说函数add中参数z的默认值,在装饰函数内部不被接受。为什么?
这很棘手,但您可以通过使用 inspect
模块
import inspect
def get_unpassed_defaults(fn, args, kwargs):
args_len = len(args) + len(kwargs)
argspec = inspect.getargspec(fn)
defaults = argspec.defaults
default_names = argspec.args[-len(defaults):]
number_of_args = len(argspec.args)
number_of_positional_args = number_of_args - len(argspec.defaults)
number_of_kwargs = number_of_args - number_of_positional_args
number_of_kwargs_passed_as_positional = len(args) - number_of_positional_args
default_values = defaults[number_of_kwargs_passed_as_positional:]
default_names = default_names[number_of_kwargs_passed_as_positional:]
defaults_dict = dict(zip(default_names, default_values))
for kwarg_name in kwargs:
if kwarg_name in defaults_dict:
defaults_dict.pop(kwarg_name)
return defaults_dict
然后在 doublefunc
中利用它:
def doublefunc(fn):
def wrapped(*arg, **kwargs):
unpassed_defaults = get_unpassed_defaults(fn, arg, kwargs)
kwargs.update(unpassed_defaults)
arg = (i if type(i) is not int else i*2 for i in arg)
for k, v in kwargs.iteritems():
kwargs[k] = v *2 if type(v) is int else v
return fn(*arg, **kwargs)
return wrapped
@doublefunc
def add(x, y, m, z=10, v='bbaa'):
print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v)
return x + y + z
print add(1, 1, "teststring", v=3)
给出:
x=2, y=2, m=teststring, z=20, v=6
24
原因是装饰器函数不查找包装函数的内部属性(在本例中为默认 kwargs)。如果您仍然想这样做,那么您只需要使用字典即可。
Won't work with Python 2 because there is no
getfullargspec
method, so for that follow @Nolen Royalty's answer
为Python3
>>> import inspect
>>> def doublefunc(fn):
def wrapped(*arg, **kwargs):
defaults = (inspect.getfullargspec(fn).kwonlydefaults)
defaults.update(kwargs)
kwargs = defaults
arg = (i if type(i) is not int else i*2 for i in arg)
for k, v in kwargs.items():
kwargs[k] = v *2 if type(v) is int else v
return fn(*arg, **kwargs)
return wrapped
>>> @doublefunc
def add(x, y, m, *args, z=10, v='bbaa'):
print('x={0}, y={1}, m= {2}, z={3}, v={4}'.format(x,y,m,z,v))
return x + y + z
>>> print(add(1, 1, 'teststring', v=1))
结果:
x=2, y=2, m= teststring, z=20, v=2
24