args或kwargs接受python参数的默认值吗?

时间:2016-04-28 03:31:46

标签: python parameters default-value

当我尝试制作测试装饰时,我发现了一个奇怪的实例。像这样的代码:

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.这意味着函数添加中的参数 z 的默认值在装饰器中不被接受功能内在。为什么?

2 个答案:

答案 0 :(得分:3)

这很棘手,但您可以通过使用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

答案 1 :(得分:2)

这样做的原因是装饰器功能不会查找包装函数的内部属性(在本例中为默认的kwargs)。如果您仍想这样做,那么您只需要使用词典。

  

不能使用Python 2,因为没有getfullargspec方法,所以关注@Nolen Royalty的答案

对于Python 3

>>> 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