__init __()为关键字参数“ bar”获得了多个值

时间:2019-05-09 14:54:07

标签: python typeerror kwargs

看起来像一个简单的模式,用于允许可变数量的位置参数以及默认名称的arg无效。 我想我可以使用**kwargs来克服这个问题,但是我做错了什么更直接的方法吗?

为什么不起作用?

class Foo(obect):
    """Baffling"""
    def __init__(self, bar=None, *args):
        pass

foo = Foo(1) #works
foo = Foo(1, bar=2) # explodes

----> 1 foo = Foo(1, bar='baz')
TypeError: __init__() got multiple values for keyword argument 'bar'

以上代码如何传递重复的bar关键字参数?

2 个答案:

答案 0 :(得分:2)

在Python中,已命名属性在未命名之后使用(例如*args)。因此,您应该将__init__更改为:

def __init__(self, *args, bar=None):

这里是示例:

class Foo(object):
    """Baffling"""
    def __init__(self, *args, bar=None):
        self.bar = bar

foo = Foo(1, bar=2) # explodes
foo.bar

会返回:

Out[...]: 2


P.S。无论如何,混合使用*args**kwargs和命名属性不是一个好主意。我建议您避免这种情况。

答案 1 :(得分:2)

命名参数既可以按位置也可以按名称传递:

>>> def foo(a):
...     print(a)
...
>>> foo(1)
1
>>> foo(a=2)
2

如果您传递位置和关键字参数,则会按顺序分配它们。在您的情况下,将1分配给第一个位置参数(bar),然后bar=2分配名为bar的参数。因此,两者都分配给名称bar,从而产生冲突。


您可以在您的命名对象之后的 之后传递其他参数:

>>> def foo(bar=None, *args):
...    print('args=%r, bar=%r' % (args, bar))
...
... foo(2, 1)  # bar=2, *args=(1,)
args=(1,), bar=2

在Python3中,您还可以仅将parameter关键字设为:

>>> def foo(*args, bar=None):
...    print('args=%r, bar=%r' % (args, bar))
...
... foo(1, bar=2)
args=(1,), bar=2

当您不接受任何可变参数时,此方法也适用:

>>> def foo(*, bar=None):
...    print('args=%r, bar=%r' % ('undefined', bar))
...
... foo(bar=2)
args='undefined', bar=2
>>> foo(1, bar=2)
TypeError: foo() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given

在Python2中,**kwargs之后仅允许*args。您可以通过从kwargs中弹出命名参数来模拟它们:

>>> def foo(*args, **kwargs):
...    bar = kwargs.pop("bar")
...    print('args=%r, bar=%r' % (args, bar))
...
... foo(1, bar=2)
args=(1,), bar=2

如果您想要仅命名参数而没有可变位置和命名参数,则必须自己引发错误:

>>> def foo(*args, **kwargs):
...    bar = kwargs.pop('bar')
...    if args or kwargs:
...        raise TypeError
...    print('args=%r, bar=%r' % ('undefined', bar))
...
... foo(bar=2)
args='undefined', bar=2
>>> foo(1, bar=2)
TypeError