Python:__init__()出现'multiple values for bar'错误的原因及疑问
为什么会出现"multiple values for keyword argument"错误?
这个问题其实是Python参数解析规则在起作用,咱们把代码拆解开来一步步看就明白了:
首先看你的类定义:
class Foo(object): """Baffling""" def __init__(self, bar=None, *args): pass
这里的bar=None是一个带默认值的位置参数(注意,它不是仅限关键字参数),而*args的作用是收集所有未被匹配的位置参数。
接下来看触发错误的调用:foo = Foo(1, bar=2)
Python解析参数的顺序是这样的:
- 先匹配位置参数:第一个传入的
1会被直接绑定到第一个位置参数bar上(self是实例自动传入的,不用手动传递); - 然后处理关键字参数:你又传了
bar=2,这时候Python发现bar已经被位置参数赋值过了,自然就会抛出TypeError: __init__() got multiple values for keyword argument 'bar'——相当于你给同一个参数传了两次值,一次用位置、一次用关键字,这肯定会冲突!
那为什么Foo(1)能正常运行?因为这个调用里,1被赋值给bar,*args收集到的是空元组,完全符合参数规则,所以没问题。
那这种写法为什么行不通?
核心原因是:在Python中,如果带默认值的参数定义在*args前面,它本质还是位置参数,只是当你没传位置值时会启用默认值。这就意味着,它既可以通过位置传递,也可以通过关键字传递,但不能同时用两种方式传——否则就会出现重复赋值的冲突。
怎么实现你想要的效果?
如果你想同时支持可变数量的位置参数,和带默认值的命名参数,有两种更合理的写法:
1. 使用仅限关键字参数(Python 3+ 推荐)
把*args放在前面,后面的参数就会变成仅限关键字参数,只能通过关键字传递:
class Foo(object): def __init__(self, *args, bar=None): pass
这样调用就不会有冲突了:
Foo(1)→args=(1,),bar=NoneFoo(1, 2, bar=3)→args=(1,2),bar=3Foo(bar='baz')→args=(),bar='baz'
2. 使用**kwargs(你提到的方法)
手动处理关键字参数,灵活性更高:
class Foo(object): def __init__(self, *args, **kwargs): bar = kwargs.pop('bar', None) # 这里可以处理args和剩下的kwargs pass
这种写法适合需要处理更多自定义关键字参数的场景。
内容的提问来源于stack exchange,提问作者Joshua Clayton
相关产品推荐
相关产品推荐

