向重写方法添加关键字参数并使用**kwarg

haw*_*k64 13 python

我是一个对象的子类,以便覆盖我想要添加一些功能的方法.我不想完全替换它或添加一个不同命名的方法,但只是通过向方法添加一个可选参数来保持与超类方法兼容.是否可以使用*args**kwargs传递超类的所有参数,并仍然添加一个带默认值的可选参数?我直观地想出了以下内容,但它不起作用:

class A(object):
    def foo(self, arg1, arg2, argopt1="bar"):
        print arg1, arg2, argopt1

class B(A):
    def foo(self, *args, argopt2="foo", **kwargs):
        print argopt2
        A.foo(self, *args, **kwargs)


b = B()
b.foo("a", "b", argopt2="foo")
Run Code Online (Sandbox Code Playgroud)

当我明确添加超类方法的所有参数时,我可以使它工作:

class B(A):
    def foo(self, arg1, arg2, argopt1="foo", argopt2="bar"):
        print argopt2
        A.foo(self, arg1, arg2, argopt1=argopt1)
Run Code Online (Sandbox Code Playgroud)

什么是正确的方法,我必须知道并明确声明所有重写的方法参数?

Ned*_*der 11

class A(object):
    def foo(self, arg1, arg2, argopt1="bar"):
        print arg1, arg2, argopt1

class B(A):
    def foo(self, *args, **kwargs):
        argopt2 = kwargs.get('argopt2', default_for_argopt2)
        # remove the extra arg so the base class doesn't complain. 
        del kwargs['argopt2']
        print argopt2
        A.foo(self, *args, **kwargs)


b = B()
b.foo("a", "b", argopt2="foo")
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用pop而不是get + del. (5认同)
  • 当没有传递`argopt2`时,你的例子失败了`KeyError`.使用`kwargs.pop('argopt2',default_for_argopt2)`是解决它的最简单方法. (4认同)

Ale*_*lli 6

什么是正确的方法,我必须知道并明确声明所有重写的方法参数?

如果你想要涵盖所有情况(而不是仅仅依靠调用者总是以你的方式做事,例如,总是只用名字传递的额外参数,从不按位置调用你)你必须编码(或动态发现) )关于你所覆盖的方法的签名的许多知识 - 这并不令人惊讶:继承是一种强大的耦合形式,而重写方法是耦合呈现的一种方式.

可以通过inspect.getargspec动态发现超类的方法参数,以确保你正确地调用它......但是如果两个类试图做同样的事情(一旦你知道你的超类的方法),这种内省技术会变得棘手接受*a和/或**kw你可以做更多的事情,而不是向上传递所有相关的论点,并希望,手指交叉,上游方法链最终做适当的大扫除之前调用一个不太宽容的版本).

当您设计一个动态应用于具有各种签名的可调用的包装器时,这样的价格可能是值得的(特别是因为在装饰器设置中,您可以安排为每个功能支付高昂的内省成本')重新装饰,而不是每次调用生成的包装器).在像你这样的情况下,你似乎不太可能成为一个有价值的技术,在那里你最好知道你的子类是什么(子类化是强耦合:盲目地做它绝对不可取!),所以你不妨拼出来明确的论点.

是的,如果超类的代码发生了巨大的变化(例如,通过改变方法签名),你也必须修改子类 - 这是继承价格的一部分.整体价格非常高,以至于新的Go编程语言完全没有它 - 迫使你应用Gang of 4的优秀建议来优先选择合成而不是继承.在Python中,完全禁止继承只是不切实际,但是清醒地和适度地使用它(并且在你做的时候接受你将在耦合方面支付的价格)仍然是可取的.