我是一个对象的子类,以便覆盖我想要添加一些功能的方法.我不想完全替换它或添加一个不同命名的方法,但只是通过向方法添加一个可选参数来保持与超类方法兼容.是否可以使用*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)
什么是正确的方法,我必须知道并明确声明所有重写的方法参数?
如果你想要涵盖所有情况(而不是仅仅依靠调用者总是以你的方式做事,例如,总是只用名字传递的额外参数,从不按位置调用你)你必须编码(或动态发现) )关于你所覆盖的方法的签名的许多知识 - 这并不令人惊讶:继承是一种强大的耦合形式,而重写方法是耦合呈现的一种方式.
你可以通过inspect.getargspec动态发现超类的方法参数,以确保你正确地调用它......但是如果两个类试图做同样的事情(一旦你知道你的超类的方法),这种内省技术会变得棘手接受*a和/或**kw你可以做更多的事情,而不是向上传递所有相关的论点,并希望,手指交叉,上游方法链最终做适当的大扫除之前调用一个不太宽容的版本).
当您设计一个动态应用于具有各种签名的可调用的包装器时,这样的价格可能是值得的(特别是因为在装饰器设置中,您可以安排为每个功能支付高昂的内省成本')重新装饰,而不是每次调用生成的包装器).在像你这样的情况下,你似乎不太可能成为一个有价值的技术,在那里你最好知道你的子类是什么(子类化是强耦合:盲目地做它绝对不可取!),所以你不妨拼出来明确的论点.
是的,如果超类的代码发生了巨大的变化(例如,通过改变方法签名),你也必须修改子类 - 这是继承价格的一部分.整体价格非常高,以至于新的Go编程语言完全没有它 - 迫使你应用Gang of 4的优秀建议来优先选择合成而不是继承.在Python中,完全禁止继承只是不切实际,但是清醒地和适度地使用它(并且在你做的时候接受你将在耦合方面支付的价格)仍然是可取的.