关于python:为什么**不在函数调用中解包kwargs?

why doesn't ** unpack kwargs in function calls?

这是困扰我一段时间的事情:

1
2
3
4
def test (*args, **kwargs):
    print target

test(foo='bar', target='baz')

我假设底部的afunc调用中的target='test'以kwargs结尾(确实如此),并且我还假设**将在函数调用中解包kwargs,因此target将作为afunc内部的关键字参数存在。没有。我知道它是以听写的形式出现的,但我需要把听写解包到参数列表中。这有可能吗?简而言之,有没有办法让*args和**kwargs消失,让实际的args和kwargs进入通话?

编辑:我整理了一个案例,在这个案例中,解包*args和**kwargs可能会有所帮助:

假设我有一个打印列表的函数:

1
2
def printList (inputList=None):
    print inputList

我希望能够传递无列表并提供默认列表:

1
2
3
4
5
6
7
8
def ensureList (listFunc):
    def wrapper (inputList=None):
        listFunc(inputList=inputList or ['a','default','list'])
    return wrapper

@ensureList
def printList (inputList=None):
    print inputList

现在我想让列表中继器变得更复杂一点:

1
2
3
@ensureList
def repeatList (inputList=None):
    print inputList*2

那很好。但现在我想重复变量:

1
2
3
@ensureList
def repeatList (times, inputList=None):
    print inputList*times

现在你可以说:

1
repeatList(5)

它将生成默认列表并重复5次。

当然,这失败了,因为包装器无法处理times参数。我当然可以这样做:

1
2
@ensureList
def repeatList (inputList=None, times=1)

但我总是要这样做:

1
repeatList(times=5)

也许在某些情况下,我想强制提供一个值,所以非关键字arg是有意义的。

当我去年第一次遇到这样的问题时,我认为一个简单的解决方案是删除包装器上的需求:

1
2
3
4
5
def ensureList (listFunc):
   "info here re: operating on/requiring an inputList keyword arg"
    def wrapper (*args, **kwargs):
        listFunc(inputList=inputList or ['a','default','list'])
    return wrapper

不过,这行不通。这就是为什么我想让args和kwargs实际扩展,或者我想有一种方法来扩展。然后无论我提供什么args和kwargs,它们实际上都会填充参数,而不是列表和dict。包装器中的文档将解释需求。如果传入inputlist,它实际上会进入,并且从包装器回调repeatlist中的inputlist是有效的。如果没有传入inputlist,它将在回调中创建带有默认列表的repeatlist。如果您的功能不关心,而是使用了*Kwargs,它将优雅地接受它,而不会有任何问题。

如果上述任何一个错误(超出了一般概念),请道歉。我在这里打出来的,还没有测试过,很晚了。


回答"为什么在函数调用中**不解包kwargs?"是:因为这是一个坏主意,开发函数的人不希望局部变量只根据调用参数出现。

所以,这不是它的工作方式,而且您肯定不希望Python这样做。

要访问函数中的target变量,可以使用:

1
2
def test(target='<default-value>', *args, **kwargs):
    print target

1
2
3
def test(*args, **kwargs):
    target = kwargs.get('target', '<default-value>')
    print target

但是,如果您希望黑客(仅限教育使用)解包**Kwargs,可以尝试:

1
2
3
4
def test(*args, **kwargs):
    for i in kwargs:
        exec('%s = %s' % (i, repr(kwargs[i])))
    print target


对于这个特殊的案例,显而易见的方法是

1
2
3
4
def test(foo=None, target=None):
    print target

test(foo='bar', target='baz')

如果要按名称访问函数内部的参数,请在参数列表中显式命名该参数。