Python 继承机制详解:从单继承到多继承与 super() 使用
本文深入解析 Python 中的继承机制,涵盖接口与实现继承的区别、单继承与方法重写原理,以及多继承中的 MRO 和钻石继承问题。重点讲解 super() 方法在解决多继承初始化重复调用中的应用,结合代码示例阐明类属性检索与实例化过程,帮助开发者掌握 Python 面向对象设计的核心概念。
Python 继承基础概念
继承
在 Python 中,所有类默认都是 object 类的子类,而 object 类本身继承自 type。
继承关系主要分为接口继承和实现继承两种类型。
接口继承是指子类使用父类的接口名称,并在子类中重新实现该方法。建议尽可能继承接口类,在子类中完成具体方法实现。这种方式鼓励对接口类进行多继承,符合接口隔离原则,有利于归一化设计。相比之下,不提倡对抽象类进行多继承。
实现继承是指子类无需实现任何方法,直接复用父类的接口和实现。这种方式会增强代码的耦合性,因此不推荐使用。
一些细节
类继承最终目的是被实例化,我们在实际开发中更多操作的是对象而非类。因此,我们需要逐步深入理解继承的具体表现。
继承与实例化的内部机制
继承的过程
继承仅是一种代码复用的手段,并不会将代码全部加载到子类的命名空间中,方法依然归属于父类。通过示例可以看出,Cat.func 实际上仍然是 Animal 的方法。更深层的理解是,func 仅仅是一个函数,它被绑定到了类 Animal 上。类只是帮助我们定位这个函数,子类通过父类找到该函数即可。
class Animal(object):
def func(self):
print("Animal.func")
class Dog(Animal):
def func(self):
print('Dog.func')
class Cat(Animal):
""" No func~ """
print(Animal.func) # <function Animal.func at 0x103f79620>
print(Cat.func) # <function Animal.func at 0x103f79620>
print(Dog.func) # <function Dog.func at 0x104073510>
实例化的过程
在实例化过程中,属性和方法并不会直接出现在实例的命名空间中,它们依然属于类本身。对象只是能够找到并调用它们。然而,当修改对象的属性时,会在对象的空间中创建同名的属性,这属于对象自身的属性。复杂的继承其本质与此相同。
class Animal(object):
def tell(self):
print('self.name:%s Animal.name %s ' % (id(self.name), id(Animal.name)))
name = 'Animal'
class Cat(Animal):
""" No func~ """
def tell(self):
super().tell()
print('self.name %s Cat.name %s '% (id(self.name), id(Cat.name)))
cat = Cat()
cat.tell()
cat.name = 'django'
cat.tell()
# self.name:4473398472 Animal.name 4473398472
# self.name 4473398472 Cat.name 4473398472
# self.name:4474859736 Animal.name 4473398472
# self.name 4474859736 Cat.name 4473398472
单继承与方法重写
单继承
越靠近当前类的方法会覆盖祖辈类的方法,这被称为方法的重写或覆盖。其原理是 Python 的属性检索机制,即从内层命名空间向外层查询。
class MyClass(object):
"""
A simple example class
"""
MyClassName = 'MyClass'
name = 'MyClass'
def func(self):
print("This is {}".format(self.__class__.name))
def get_name(self):
print(self.name)
class MySonClass(MyClass):
MySonClass = 'MySonClass'
name = 'MySonClass' # 属性的重写
def get_name(self):
super().get_name()
print('我重写了父类的get_name方法,上面是父类的方法,我来自子类!')
person1 = MyClass()
person2 = MySonClass()
person1.func()
person2.func() # 方法的实现继承 自己没有,会直接调用父类的方法。但是使用的属性还是自己的。
print('*'*40)
person1.get_name()
person2.get_name() # 方法的接口继承,在子类中重写了这个方法。
# 结果
#------------------------------
# This is MyClass
# This is MySonClass
# ****************************************
# MyClass
# MySonClass
# 我重写了父类的get_name方法,上面是父类的方法,我来自子类!
多继承与 MRO 顺序
多继承
从形式上看,类的继承列表可以包含一个或多个类。当继承列表仅有一个类时,称为单继承;当包含多个类时,称为多继承。
新式类的继承方式采用广度优先继承,而经典类的继承方式采用深度优先继承。
类继承的顺序可以通过类的 __mro__ 属性查看。
钻石继承
class A(object):
m = 'a'
class B(A):
m = 'b'
class C(A):
m = 'c'
class D(B,C):
# m = 'd'
pass
x = D()
print(x.m)
# D 的实例化对象如果获取 m 属性会优先的寻找自己的命名空间,查找顺序为 D -> B -> C -> A
super() 方法与钻石继承
super() 方法
语法格式为 super(类, 实例化对象).父类的方法。
当 super() 方法在类的内部使用时,甚至不需要提供任何参数。
在多继承中使用 super() 方法时,执行的不再是直接父类的方法,而是 __mro__ 列表中的下一个类的方法。
super() 的引入是为了解决多继承中初始化方法被重复调用的问题(当直接使用类名调用方法时)。
当使用 super() 方法执行“父类”(即 __mro__ 列表中的下一个类)的方法时,能够确保调用顺序的正确性。
# 钻石继承中的 重复调用问题
# 注意 继承的查找顺序~ 使用super()将按照 mro 顺序执行
class Grand(object):
def __init__(self, name):
self.name = name
print("class Grand ")
class SonLeft(Grand):
def __init__(self, age, name):
self.age = age
Grand.__init__(self, name) # 注释调 跑一跑 看一看
# super().__init__(age, name)
print("class SonLeft")
class SonRight(Grand):
def __init__(self, gender, name):
self.gender = gender
Grand.__init__(self, name) # 注释调 跑一跑 看一看
# super().__init__(name)
print("class SonRight")
class GrandSon(SonLeft, SonRight):
def __init__(self, name, age, gender):
# super().__init__(age, name)
SonLeft.__init__(self, age, name) # 注释调 跑一跑 看一看
SonRight.__init__(self, gender, name) # 注释调 跑一跑 看一看
self.gender = gender
grand_son = GrandSon("Monkey", 18, "男")
以上就是对 Python 中继承机制的详细解析,更多关于 Python 继承的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









