Python中的重写与虚拟类方法
根据我的理解,在Python中,父类的方法是可以被覆盖的。也就是说,所有类的方法默认都是虚拟的,下面的代码可以看出这一点。
class Parent(object):
def Method1(self):
print 'FOO'
class Child(Parent):
def Method1(self):
print 'BAR'
myInstance = Child()
#Overridden method is called
myInstance.Method1() # BAR
#Is it possible to do the following?
myInstance.{something} # FOO
问题:
从子类的实例
myInstance
中,能否调用父类的方法Parent.Method1()
?有没有办法让
def Parent.Method1(self)
不被同名但参数不同的方法def Child.Method1(self,x,y)
覆盖?
3 个回答
除了使用 super
之外,还有一些明确的选项可以绕过方法解析顺序(MRO):
# Explicitly call Baseclass.Method1, passing your instance as the argument
BaseClass.Method1(myInstance)
# Call Method1 on the (first) base class of the instance.
# This is ugly; don't actually write code like this
# x.__class__.__bases__[0] is just another way to get a reference
# to BaseClass; otherwise, this is identical to the previous option,
# hence the need to repeat myInstance
myInstance.__class__.__bases__[0].Method1(myInstance)
从子类的实例 myInstance 调用父类的方法 Parent.Method1() 是可能的吗?
你可以直接调用这个方法,并把你的实例作为第一个参数传进去:
>>> class A(object):
... def foo(self):
... print 'foo'
...
>>> class B(A):
... def foo(self):
... print 'bar'
...
>>> b = B()
>>> A.foo(b)
foo
有没有办法让 def Parent.Method1(self) 不被同名但参数不同的方法 def Child.Method1(self,x,y) 覆盖掉?
可以的,你可以使用 super 来直接调用父类的方法:
>>> class A(object):
... def foo(self, bar='bar'):
... print bar
...
>>> class B(A):
... def foo(self):
... super(B, self).foo(bar='foo')
...
>>> B().foo()
foo
>>> A().foo()
bar
你可以通过使用 super()
函数来调用父类的方法,这个函数会在方法解析顺序(MRO)中寻找下一个方法:
class Child(BaseClass):
def Method1(self):
print 'BAR'
super(Child, self).Method1() # will call BaseClass.Method1
这里的 self
是实例的引用,所以 super(type(myInstance), myInstance).Method1()
也能在方法外部达到同样的效果。
或者你可以直接访问类中的未绑定方法,并明确传入实例;这样在方法外部也能做到:
BaseClass.Method1(myInstance)
因为这时候你是直接引用了类中的方法属性,并将实例作为明确的 self
传入这个方法;在 Child
类的方法内部,你会使用 self
。
在 Python 中,属性(方法也是属性)是通过名字查找的,这种查找会先在实例中找,然后在实例的类中找,再到每个父类中查找,直到找到匹配的为止;方法的签名在这里并不影响查找。
你仍然可以在子类中重写一个方法,改变它的签名,但通过使用关键字参数,依然可以与父类的方法保持 兼容:
class Child(BaseClass):
def Method1(self, x=None, y=None):
if x is None and y is None:
# arguments left at defaults, call base method
return super(Child, self).Method1()
print 'BAR: {} and {}'.format(x, y)
现在如果你调用 myInstance.Method1()
,不传入任何参数,就会调用 BaseClass.Method1()
方法;如果传入参数,情况就会有所不同。