可以说我有
class Super():
def method1():
pass
class Sub(Super):
def method1(param1, param2, param3):
stuff
它是否正确?将对method1的调用始终转到子类吗?我的计划是让2个子类分别覆盖具有不同参数的method1
Answers:
在Python中,方法只是附加到该类的字典中的键/值对。当您从基类派生一个类时,您实际上是在说,方法名称将先进入派生类字典,然后再进入基类字典。为了“重写”方法,您只需在派生类中重新声明该方法。
那么,如果您在派生类中更改重写方法的签名,该怎么办?如果调用是在派生实例上进行的,则一切正常,但是如果您在基实例上进行调用,则会出现错误,因为基类对该相同的方法名称使用不同的签名。
但是,在很多情况下,您希望派生类方法具有其他参数,并且希望方法调用也能在没有错误的基础上工作。这称为“ Liskov替换原理”(或LSP),它可以保证如果人们从基本实例切换到派生实例,反之亦然,则不必修改代码。要在Python中执行此操作,您需要使用以下技术来设计基类:
class Base:
# simply allow additional args in base class
def hello(self, name, *args, **kwargs):
print("Hello", name)
class Derived(Base):
# derived class also has unused optional args so people can
# derive new class from this class as well while maintaining LSP
def hello(self, name, age=None, *args, **kwargs):
super(Derived, self).hello(name, age, *args, **kwargs)
print('Your age is ', age)
b = Base()
d = Derived()
b.hello('Alice') # works on base, without additional params
b.hello('Bob', age=24) # works on base, with additional params
d.hello('Rick') # works on derived, without additional params
d.hello('John', age=30) # works on derived, with additional params
上面将打印:
你好爱丽丝 你好鲍勃 你好里克 你的年龄是无 你好约翰 你30岁。 玩这个代码
*args
吗?如果没有,类似的代码d.hello("John", "blue", age=30)
将无法工作。我的意思是,一般来说,位置args应该始终在kwargs之前定义
Python将允许这样做,但是如果method1()
打算从外部代码执行,则您可能需要重新考虑这一点,因为它违反了LSP,因此不一定总是能正常工作。
Super.method1(param1=None, param2=None, param3=None)
如果在子类上被定义为Sub.method1(param1, param2, param3)
正确,那么它仍然会违反LSP ?由于属性在一种情况下是必需的,而在另一种情况下则不是。因此,根据我的理解,在不更改子类接口的情况下,不违反LSP的唯一方法是在父级上使用没有默认值的参数。我对此是正确的还是对LSP的解释过度了?
method1
is时__init__
,不适用于LSP
在python中,所有类方法都是“虚拟的”(就C ++而言)。因此,对于您的代码,如果要调用method1()
超类,则必须为:
class Super():
def method1(self):
pass
class Sub(Super):
def method1(self, param1, param2, param3):
super(Sub, self).method1() # a proxy object, see http://docs.python.org/library/functions.html#super
pass
方法签名确实很重要。您不能调用这样的方法:
sub = Sub()
sub.method1()
如果可以使用默认参数,则可以执行以下操作:
>>> class Super():
... def method1(self):
... print("Super")
...
>>> class Sub(Super):
... def method1(self, param1="X"):
... super(Sub, self).method1()
... print("Sub" + param1)
...
>>> sup = Super()
>>> sub = Sub()
>>> sup.method1()
Super
>>> sub.method1()
Super
SubX
它将起作用:
>>> class Foo(object):
... def Bar(self):
... print 'Foo'
... def Baz(self):
... self.Bar()
...
>>> class Foo2(Foo):
... def Bar(self):
... print 'Foo2'
...
>>> foo = Foo()
>>> foo.Baz()
Foo
>>>
>>> foo2 = Foo2()
>>> foo2.Baz()
Foo2
但是,一般不建议这样做。看看S.Lott的答案:具有相同名称和不同参数的方法是代码气味。
是。调用“ method1”将始终转到子类。Python中的方法签名仅包含名称,而不包含参数列表。