派生类可以覆盖其基类的方法。因为方法在调用同一对象的其他方法时没有特殊权限,所以调用同一基类中定义的另一个方法的基类方法最终可能会调用覆盖它的派生类的方法。
这是怎么发生的?有人能用一个简单的例子来说明这个概念吗?
答案 0 :(得分:29)
以下是您要求的示例。这会打印chocolate
。
class Base:
def foo(self):
print("foo")
def bar(self):
self.foo()
class Derived(Base):
def foo(self):
print("chocolate")
d = Derived()
d.bar() # prints "chocolate"
打印字符串chocolate
而不是foo
,因为Derived
会覆盖foo()
函数。即使在bar()
中定义了Base
,它最终会调用Derived
foo()
的{{1}}实现,而不是Base
实现。
答案 1 :(得分:15)
当对类的实例执行属性查找时,类字典和将按特定顺序搜索其基类的字典(请参阅:Method Resolution Order)对于适当的方法。找到的内容 first 将被调用。
使用以下Spam
示例:
class Spam:
def produce_spam(self):
print("spam")
def get_spam(self):
self.produce_spam()
class SuperSpam(Spam):
def produce_spam(self):
print("super spam")
Spam
定义了函数produce_spam
和get_spam
。它们位于Spam.__dict__
(类命名空间)中。通过继承,子类SuperSpam
可以访问这两种方法。 SuperSpam.produce_spam
不会替换Spam.produce_spam
,只有在其中一个实例上查找名称'produce_spam'
时才会找到它。
本质上,继承的结果是,如果在对子类进行属性查找之后,在子类中找不到该属性,那么任何基类的字典也将被搜索。字典。
首次使用:
调用函数get_spam
时
s = SuperSpam()
s.get_spam()
大致的事件序列如下:
SuperSpam
__dict__
get_spam
。SuperSpam
s __dict__
中找不到它,因此请查看其基类(mro
链)的字典。 Spam
位于mro
链的下一位,因此get_spam
位于Spam
字典中。现在,produce_spam
在get_spam
的正文中self.produce_spam
SuperSpam
时,self
的序列缩短了:
__dict__
的{{1}} produce_spam
produce_spam
{/ 1}}。 __dict__
首先在{{1}}中找到,以便获取。
答案 2 :(得分:8)
class Base():
def m1(self):
return self.m2()
def m2(self):
return 'base'
class Sub(Base):
def m2(self):
return 'sub'
b = Base()
s = Sub()
print(b.m1(), s.m1())
打印“base sub”
答案 3 :(得分:1)
为了说明它的工作原理,请考虑以下两个类:
class Parent(object):
def eat(self):
print("I don't want to eat that {}.".format(self.takefrompocket()))
def takefrompocket(self):
return 'apple'
def __getattribute__(self, name):
print('Looking for:', name)
method_to_use = object.__getattribute__(self, name)
print('Found method:', method_to_use)
return method_to_use
class Child(Parent):
def takefrompocket(self):
return 'salad'
__getattribute__
方法负责属性查找的新样式类(如python3中的所有类)。它只是print
实现了每个查找的功能 - 通常您不希望也不应该自己实现。如果您真的感兴趣,查找遵循pythons method resolution order (MRO)。
>>> some_kid = Child()
>>> some_kid.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Child object at 0x0000027BCA4EEA58>>
Looking for: takefrompocket
Found method: <bound method Child.takefrompocket of <__main__.Child object at 0x0000027BCA4EEA58>>
I don't want to eat that salad.
因此,当您要使用eat
时,它会在此示例中使用Parent.eat
。但self.takefrompocket
使用了Child
。
>>> some_parent = Parent()
>>> some_parent.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Parent object at 0x0000027BCA4EE358>>
Looking for: takefrompocket
Found method: <bound method Parent.takefrompocket of <__main__.Parent object at 0x0000027BCA4EE358>>
I don't want to eat that apple.
这两种方法均来自Parent
。继承的类不会(通常)干扰他们的祖先!
答案 4 :(得分:-3)
如果您的子类没有实现该方法,请引发异常!
class Base(object):
def something (self):
raise ('Not implemented')