关于python:为什么不能在子类的__init__中传递* args和** kwargs

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'

变成像super(MyFoo, self).__init__(args, kwargs)一样

结果是:

1
2
3
('Python', 2.7) {'stack': 'overflow'}
I think something is being called here
('Python', 2.7) {'stack': 'overflow'}

出于某些令人震惊的原因,我质疑这一点:在上面的例子中,什么是对的和错的?在现实生产中,什么是可以做的,什么是不可以做的?


您的Foo.__init__()不支持任意关键字参数。您可以在其签名中添加**kw,使其接受这些签名:

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

关键字参数只与具有完全匹配关键字名称的参数匹配;您的Foo方法需要有Pythonstack关键字参数。如果找不到匹配的关键字参数,但**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])

现在您必须向MyFoo传递两个或多个参数,以便调用工作。

本质上,super().methodname返回对绑定方法的引用;从那里开始,它是一个普通方法,因此需要传入任何方法都可以接受的参数。如果您的方法不接受关键字参数,您将得到一个异常。


原因是所有的论点都已经被解包成了夸格斯语,现在这是一个口述。你试图把它传递给一个正态变量。

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