python 多态和 super 用法

简介: python 多态和 super 用法

python 中的多态实现非常简单,只要是在子类中实现和父类同名的方法,便能实现多态,如果想在子类中调用父类的方法,有多种方法,但是当涉及菱形继承等问题是,super 就成为了比较好的解决方案。

普通继承


对于比较简单的继承关系,通常在子类中有两种方法来执行父类的方法,示例如下。

基类:


class Base(object):  
def __init__(self):  
print("init Base")


示例 1:


class A(Base):
    def __init__(self):
        # 通过父类显式执行
        Base.__init__(self)
        print("init A")
a = A()


输出:


init Base
init A


示例 2:


class B(Base):
    def __init__(self):
        # 调用 super
        super(B, self).__init__()
        print("init B")
b = B()


输出:


init Base
init B


可以看到,两种方法都可以调用父类的方法对父类进行初始化。需要注意的是,两种方法都要传入 self,但是在子类中调用父类的 super 中传入的 self是子类对象。


菱形继承


当有多重继承,特别是菱形继承时,这两种方法就有区别了,示例如下。

示例 1:


class Base(object):
    def __init__(self):
        print("init Base")
class A(Base):
    def __init__(self):
        Base.__init__(self)
        print("init A")
class B(Base):
    def __init__(self):
        Base.__init__(self)
        print("init B")
class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print("init C")
c = C()


输出:


init Base
init A
init Base
init B
init C


可以看到,Base 被 init 了两次,至于其缺陷,在 C++ 中就已经讨论过了,反正就是不符合我们的预期,不想这种实现。C++ 中通过虚继承解决菱形继承问题,在 python 中可以使用 super 规避这种缺陷。

示例2:


class Base(object):
    def __init__(self):
        print("init Base")
class A(Base):
    def __init__(self):
        super(A, self).__init__()
        print("init A")
class B(Base):
    def __init__(self):
        super(B, self).__init__()
        print("init B")
class C(A, B):
    def __init__(self):
        super(C, self).__init__()
        print("init C")
c = C()


输出


init Base
init B
init A
init C


运行这个新版本后,你会发现每个 __init__() 方法只会被调用一次了。

为了弄清它的原理,我们需要花点时间解释下 python 是如何实现继承的。对于你定义的每一个类,python 会计算出一个所谓的方法解析顺序(MRO)列表。 这个 MRO 列表就是一个简单的所有基类的线性顺序表。我们可以看一下 C 的 MRO 表。


>>> C.__mro__ 
(__main__.C, __main__.A, __main__.B, __main__.Base, object)


为了实现继承,python 会在 MRO 列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

而这个 MRO 列表的构造是通过一个 C3 线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的 MRO 列表并遵循如下三条准则:

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类

必须牢记:MRO 列表中的类顺序会让你定义的任意类层级关系变得有意义。

当使用 super() 函数时,python 会在 MRO 列表上继续搜索下一个类(这是一种嵌套实现)。  只要每个重定义的方法统一使用 super() 并只调用它一次, 那么控制流最终会遍历完整个 MRO 列表,每个方法也只会被调用一次。 这也是为什么在第二个例子中你不会调用两次 Base.__init__() 的原因。换句话说,super 调用了次且仅有一次所有的父类。

由于 super 递归调用的会继续搜索的特性,可能会出现一些意向不到的效果,比如下面这个例子:


class A(object):
    def spam(self):
        print('A.spam')
        super(A, self).spam()
class B(object):
    def spam(self):
        print('B.spam')
class C(A, B):
    pass
c = C()
c.spam()
C.__mro__


输出


A.spam
B.spam
(__main__.C, __main__.A, __main__.B, object)


为啥 c.spam() 会同时调用 A 和 B 的 spam()?其实看到 MRO 顺序就明白了:

  • 首先在 c 的类 C 中查找 spam 方法,没有找到就查找 A 中的 spam 方法。
  • 调用 A 中的 spam 方法,然后遇到 A 的 super 调用,继续在 MRO 顺序表中查找 spam 方法。注意,这里本来调用了 A 的 spam 就应该返回的,但是 super 的存在,导致了继续递归。
  • 遇到 B 的 spam 方法,调用,结束。

super 的使用


对于 python2 和 python3,super 的用法有一些区别:

原因:

  • python2 没有默认继承 object
  • python3 默认全部继承 object 类,都是新式类

用法区别:

  • python2: super(开始类名,self).函数名()
  • python3:super().函数名()
目录
相关文章
|
10月前
|
机器学习/深度学习 PyTorch 算法框架/工具
python torch基础用法
本教程系统讲解PyTorch基础,涵盖张量操作、自动求导、神经网络构建、训练流程、GPU加速及模型保存等核心内容,结合代码实例帮助初学者快速掌握深度学习开发基础,是入门PyTorch的实用指南。
899 6
|
12月前
|
Go 调度 Python
Golang协程和Python协程用法上的那些“不一样”
本文对比了 Python 和 Go 语言中协程的区别,重点分析了调度机制和执行方式的不同。Go 的协程(goroutine)由运行时自动调度,启动后立即执行;而 Python 协程需通过 await 显式调度,依赖事件循环。文中通过代码示例展示了两种协程的实际运行效果。
438 7
|
人工智能 数据库连接 API
掌握Python的高级用法:技巧、技术和实用性示例
本文分享了Python的高级用法,包括生成器、装饰器、上下文管理器、元类和并发编程等。生成器通过`yield`实现懒加载序列;装饰器用于增强函数功能,如添加日志或性能分析;上下文管理器借助`with`语句管理资源;元类动态定制类行为;并发编程利用`threading`和`asyncio`库提升任务执行效率。掌握这些高级概念可优化代码质量,解决复杂问题,提高程序性能与可维护性。
378 6
|
Python
Python教程:os 与 sys 模块详细用法
os 模块用于与操作系统交互,主要涉及夹操作、路径操作和其他操作。例如,`os.rename()` 重命名文件,`os.mkdir()` 创建文件夹,`os.path.abspath()` 获取文件绝对路径等。sys 模块则用于与 Python 解释器交互,常用功能如 `sys.path` 查看模块搜索路径,`sys.platform` 检测操作系统等。这些模块提供了丰富的工具,便于开发中处理系统和文件相关任务。
623 14
|
SQL Oracle 关系型数据库
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法
|
SQL Oracle 关系型数据库
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法
本文来自YashanDB官网,介绍如何处理Oracle客户端sql*plus中使用@@调用同级目录SQL脚本的场景。崖山数据库23.2.x.100已支持@@用法,但旧版本可通过Python脚本批量重写SQL文件,将@@替换为绝对路径。文章通过Oracle示例展示了具体用法,并提供Python脚本实现自动化处理,最后调整批处理脚本以适配YashanDB运行环境。
|
设计模式 Python
简单分享下python多态
多态是面向对象编程中至关重要的特性,通过继承和方法重写,可以实现代码复用和灵活扩展。Python的鸭子类型进一步增强了多态的灵活性,使得代码更具适应性。通过理解和应用多态,可以写出更为简洁、优雅和高效的代码。
365 9
|
Python
Python三引号用法与变量详解
本文详细介绍了Python中三引号(`"""` 或 `'''`)的用法,包括其基本功能、如何在多行字符串中使用变量(如f-string、str.format()和%操作符),以及实际应用示例,帮助读者更好地理解和运用这一强大工具。
2874 2
|
缓存 测试技术 开发者
深入理解Python装饰器:用法与实现
【10月更文挑战第7天】深入理解Python装饰器:用法与实现
227 1
|
传感器 大数据 数据处理
深入理解Python中的生成器:用法及应用场景
【10月更文挑战第7天】深入理解Python中的生成器:用法及应用场景
764 1

推荐镜像

更多