繁体   English   中英

更改子类中的默认构造函数参数值(从父类继承)

[英]Change default constructor argument value (inherited from parent class) in subclass

我有一个Parent class 属性的默认值arg2 我想创建一个子类Child ,它对同一属性具有不同的默认值。 我需要在Child中使用*args**kwargs

我尝试了以下方法,但它不起作用:

class Parent(object):
    def __init__(self, arg1='something', arg2='old default value'):
        self.arg1 = arg1
        self.arg2 = arg2

        print('arg1:', self.arg1)
        print('arg2:', self.arg2)

class Child(Parent):
    def __init__(self, *args, **kwargs):
        super(Child, self).__init__(*args, **kwargs)
        self.arg2 = kwargs.pop('arg2', 'new value')

这是行不通的。 事实上,我得到:

>>> c = Child()
arg1: something
arg2: default value # This is still the old value
>>> c.arg2
'new value' # Seems more or less ok

>>> c = Child('one', 'two')
arg1: one
arg2: two
>>> c.arg2
'new value' # This is wrong, it has overridden the specified argument 'two'

您需要在kwargs设置默认值,然后再将其传递给super() 这很棘手,因为您需要确保args也没有相同的值:

class Child(Parent):
    def __init__(self, *args, **kwargs):
        if len(args) < 2 and 'arg2' not in kwargs:
            kwargs['arg2'] = 'new value'
        super(Child, self).__init__(*args, **kwargs)

但是,这取决于知道要填充多少参数。 您必须使用super().__init__功能才能在一般情况下正常工作:

from inspect import getargspec

class Child(Parent):
    def __init__(self, *args, **kwargs):
        super_init = super().__init__
        argspec = getargspec(super_init)
        arg2_index = argspec.args.index('arg2') - 1  # account for self
        if len(args) < arg2_index and 'arg2' not in kwargs:
            kwargs['arg2'] = 'new value'
        super(Child, self).__init__(*args, **kwargs)

您最好指定所有默认值,而不是:

class Child(Parent):
    def __init__(self, arg1='something', arg2='new value'):
        super(Child, self).__init__(arg1=arg1, arg2=arg2)

您实际上已经更改了类的签名。 基本上,具有:

def foo(a=1, b=2):
   ...

您可以按职位关键字致电:

foo(2, 3)
foo(a=2, b=3)

带有:

def bar(**kwargs):
   ...

您不能再使用位置参数调用:

bar(2, 3)  # TypeError!

您的实际代码还有其他麻烦,因为那里有*args ,它们会耗尽所有位置参数。


我可以给您的最可靠的建议是,当您重写方法时,保留签名:

class Child(Parent):
    def __init__(self, arg1='something', arg2='new value'):
        super(Child, self).__init__(arg1=arg1, arg2=arg2)

这(不幸的是)不是您可能想要的DRY(不要重复自己)-您必须两次指定'something' 您可以将其转换为全局常量,也可以更改Parent.__init__的签名。

另外,您可以进行一系列自省以使用父类的签名,以确保您以正确的方式传递正确的参数,但是我非常怀疑这样做是否值得。

我对这两种解决方案都不满意,并提出了下面的解决方案。 它将默认值引入为 class 属性,如果默认值为 None,则加载这些属性:

#!/usr/bin/env python3

class Parent:
    _arg1_default = 'something'
    _arg2_default = 'old default value'

    def __init__(self, arg1=None, arg2=None):
        if arg1 is None:
            arg1 = self._arg1_default
        if arg2 is None:
            arg2 = self._arg2_default

        self.arg1 = arg1
        self.arg2 = arg2

        print('arg1:', self.arg1)
        print('arg2:', self.arg2)


class Child(Parent):
    _arg2_default = 'new value'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


if __name__ == '__main__':
    print('Call Parent without arguments (use defaults)')
    parent = Parent()
    print('Call Child without arguments (use defaults)')
    child = Child()
    print('Call 2nd Child with custom arguments ("one", "two")')
    child2 = Child('one', 'two')
    print('Query arg2 of 2nd child')
    print(child2.arg2)

产量:

Call Parent without arguments (use defaults)
arg1: something
arg2: old default value
Call Child without arguments (use defaults)
arg1: something
arg2: new value
Call 2nd Child with custom arguments ("one", "two")
arg1: one
arg2: two
Query arg2 of 2nd child
two   

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM