args 或 kwargs 是不是接受 python 参数的默认值? [复制]

Posted

技术标签:

【中文标题】args 或 kwargs 是不是接受 python 参数的默认值? [复制]【英文标题】:Is python parameters' default value accepted by args or kwargs? [duplicate]args 或 kwargs 是否接受 python 参数的默认值? [复制] 【发布时间】:2016-08-22 15:12:00 【问题描述】:

我在尝试制作测试装饰器时发现了一个奇怪的例子。代码如下:

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的默认值在decorator中不被接受功能内。为什么?

【问题讨论】:

print add(1, 1, 'teststring') 返回 12!这是正确的!您确定收到 14 吗?你为什么要 24 岁? @EbraHim 它在我的计算机上返回 14(Python 2.7,Ubuntu Kylin)。你在使用 Python 3 吗? 对不起,我的错。它返回 14。 【参考方案1】:

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

【讨论】:

感谢您的帮助,但正如 vanza 所说,这是一个重复的问题。装饰器的内部 warpper 无法访问 warpped 函数。也感谢 vanza! @HenryJohn 但装饰器的内部包装器 确实 可以访问,如我所演示的。我的代码做你想做的事。【参考方案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

【讨论】:

您可以(并且应该)在 Python 2 中将括号与 print 语句一起使用,因此代码在两者中都可以使用。对于小型字典,您也可以在两者中使用.items(),它只会在 2 中使用 Eager 并在 3 中使用惰性。对于大型字典,我通常只是从 six 库中导入 iteritems getfullargspec 在 Python 2 中不存在。 谢谢,所以这只是 Python 3 的解决方案。@Nolen Royalty

以上是关于args 或 kwargs 是不是接受 python 参数的默认值? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

*args and **kwargs

Python3中参数*args和**kwargs介绍

dbt 宏 - 使用 *args/**kwargs

python函数基础

默认参数,不固定参数 *args,**kwargs

python中*args 和**kwargs的用法