Why can't pass *args and **kwargs in __init__ of a child class
为了理解"args"和"kwargs",我在回答"args"和"kwargs"这个问题时做了一些调查。
下面的答案引起了我的注意,这是:
1 2 3 4 5 6 7 8 9 10 | class Foo(object): def __init__(self, value1, value2): # do something with the values print value1, value2 class MyFoo(Foo): def __init__(self, *args, **kwargs): # do something else, don't care about the args print 'myfoo' super(MyFoo, self).__init__(*args, **kwargs) |
我在这个例子中做了一些尝试,并以这种方式运行代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Foo(object): def __init__(self, value1, value2): # do something with the values print 'I think something is being called here' print value1, value2 class MyFoo(Foo): def __init__(self, *args, **kwargs): # do something else, don't care about the args print args, kwargs super(MyFoo, self).__init__(*args, **kwargs) foo = MyFoo('Python', 2.7, stack='overflow') |
我得到这个:
1 2 3 | [...] super(MyFoo, self).__init__(*args, **kwargs) TypeError: __init__() got an unexpected keyword argument 'stack' |
变成像
结果是:
1 2 3 | ('Python', 2.7) {'stack': 'overflow'} I think something is being called here ('Python', 2.7) {'stack': 'overflow'} |
出于某些令人震惊的原因,我质疑这一点:在上面的例子中,什么是对的和错的?在现实生产中,什么是可以做的,什么是不可以做的?
您的
1 2 3 4 | class Foo(object): def __init__(self, value1, value2, **kw): print 'I think something is being called here' print value1, value2, kw |
关键字参数只与具有完全匹配关键字名称的参数匹配;您的
如果子类知道父类只有位置参数,则可以始终传入位置参数:
1 2 3 4 5 6 7 | class MyFoo(Foo): def __init__(self, *args, **kwargs): # do something else, don't care about the args print args, kwargs while len(args) < 2: args += kwargs.popitem() super(MyFoo, self).__init__(*args[:2]) |
现在您必须向
本质上,
原因是所有的论点都已经被解包成了夸格斯语,现在这是一个口述。你试图把它传递给一个正态变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | def bun(args,kwargs): print 'i am here' print kwargs def fun(*args,**kwargs): print kwargs bun(*args,**kwargs) fun(hill=3,bi=9) # will fail. def bun(*args,**kwargs): print 'i am here' print kwargs def fun(*args,**kwargs): print kwargs bun(*args,**kwargs) # will work. fun(hill=3,bi=9) |
尝试在进行修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Foo(object): def __init__(self, *value1, **value2): # do something with the values print 'I think something is being called here' print value1, value2 class MyFoo(Foo): def __init__(self, *args, **kwargs): # do something else, don't care about the args print args, kwargs super(MyFoo, self).__init__(*args, **kwargs) foo = MyFoo('Python', 2.7, stack='overflow' |
应该工作!
当你这样做的时候:
1 | super(MyFoo, self).__init__(*args, **kwargs) |
这与根据代码的工作方式执行此操作的情况相同:
1 | super(MyFoo, self).__init__("python", 2.7, stack="overflow") |
但是,foo的init函数(myfoo从中继承)不支持名为"stack"的关键字参数。
我认为值得补充的是,这可以用来简化子类中的初始签名。位置参数是从左到右排列的,所以如果你把它们添加到前面,把其余的传递给args和kwargs,你就可以避免错误,避免忘记把它们显式地添加到每个孩子身上。这里有一些关于这是否是可接受的异常的讨论,"显式优于隐式"。对于深度层次结构中的长参数列表,这可能更清晰,更容易维护。
为了修改这个例子,我在myfoo的前面添加了not-for-foo,并通过super传递其余的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | class Foo(object): def __init__(self, a_value1, a_value2, a_stack=None, *args, **kwargs): """do something with the values""" super(Foo, self).__init__(*args, **kwargs) # to objects constructor fwiw, but object.__init__() takes no args self.value1 = a_value1 self.value2 = a_value2 self.stack = a_stack return def __str__(self): return ', '.join(['%s: %s' % (k, v) for k, v in self.__dict__.items()]) class MyFoo(Foo): def __init__(self, not_for_Foo, *args, **kwargs): # do something else, don't care about the args super(MyFoo, self).__init__(*args, **kwargs) self.not_for_Foo = not_for_Foo # peals off self.myvalue1 = 'my_' + self.value1 # already set by super if __name__ == '__main__': print 'Foo with args' foo = Foo('Python', 2.7, 'my stack') print foo print ' MyFoo with kwargs' myfoo = MyFoo('my not for foo', value2=2.7, value1='Python', stack='my other stack') print myfoo $ python argsNkwargs.py Foo with args value2: 2.7, value1: Python, stack: my stack MyFoo with kwargs myvalue1: my_Python, not_for_Foo: my not for foo, value2: 2.7, value1: Python, stack: my other stack |
-LRM