红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 编程语言
  3. Python
  4. 正文

Python学习笔记18:类II

2021年4月1日 316点热度 0人点赞 0条评论

在Python学习笔记9:类中我们介绍了一些基础的类定义和使用方式,我们这里用OOP的原则对其进行重新审视,并且补充没有说到的部分。

封装

关于封装,在前文中我们介绍了简单的封装方式,即如何用前缀_或__来实现类似的访问修饰符的作用。

但有一点我们没有说明,即如何实现类方法。

我们知道,类方法和对象方法最大的不同是不依赖于对象的属性,当然也不存在对象指针或引用。

class sampleCls:
    @classmethod
    def classMethod(cls):
        print(cls)
        print("this is a class method")
​
    def objMethod(self):
        print(self)
        print("this is a object method")
​
a = sampleCls()
a.objMethod()
sampleCls.classMethod()
# <__main__.sampleCls object at 0x000001F963AF88E0>
# this is a object method
# <class '__main__.sampleCls'>
# this is a class method

从上边我们可以看到,通过使用函数修饰符classmethod,我们定义了一个类方法classMethod。

而最终的打印结果也表明了这两个方法持有的引用分别是对象和类。

继承

在继承上,Python与传统编程语言有很大不同,它支持多继承。

我们来看一个稍显复杂的例子:

class A:
    def __init__(self):
        print("A.__init__()")
​
class B(A):
    def __init__(self):
        super().__init__()
        print("B.__init__()")
​
class C(A):
    def __init__(self):
        super().__init__()
        print("C.__init__()")
​
class D(B,C):
    def __init__(self):
        super().__init__()
        print("D.__init__()")
​
d = D()
print(D.__mro__)
# A.__init__()
# C.__init__()
# B.__init__()
# D.__init__()
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

从输出我们可以观察到,这个继承关系创建过程是A>C>B>D,而且这其中虽然C和B都继承了A,但解释器很聪明,只创建了一次A。

我们这里除了在构造函数中输出以观察构建过程,还通过D.__mro__输出了一个继承关系链。

关于MRO,《Python Cookbook》一书是这么解释的:

对于你定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的所有基类的线性顺序表。

我画了一个简单的类图来说明上边的继承关系和MRO检索顺序:

image-20210328200926446

关于MRO检索顺序,《Python Cookbook》是这么说的:

  • 子类会先于父类被检查

  • 多个父类会根据它们在列表中的顺序被检查

  • 如果对下一个类存在两个合法的选择,选择第一个父类

我们这个例子已经显得很混乱了,如果遇到继承层级更复杂的局面,要理清继承链的难度可想而知。我的建议是使用clsName.__mro__查看解释器实际的继承链,再结合UML图理清继承关系。

至于这么做的必要性,如果你的继承链上有同名函数互相覆盖,那理清继承链才能确切知道哪个会覆盖哪个。

这种问题在算法中被称作“菱形问题”,在Python学习笔记29:继承的优缺点中有详细的说明。

多态

说到多态,其核心内容肯定是抽象类和接口,而在Python中这是一回事。

from abc import ABCMeta, abstractmethod
​
​
class Base(metaclass=ABCMeta):
    @abstractmethod
    def fly(self):
        '''这是一个抽象方法'''
        pass
​
​
class SubBase(Base):
    def fly(self):
        print("I can fly")
​
​
# base = Base() 会报错,因为抽象类不能实例化
base = SubBase()
base.fly()
# I can fly

借助abc模块,我们可以实现一个抽象基类,接口与之类似,因为Python是支持多继承的。

  • 这里通过指定元类metaclass=ABCMeta的方式已经过时,最新的方式更为简洁,只要继承abc.ABC类即可。

  • 关于抽象基类的更深入内容,可以阅读Python学习笔记28:从协议到抽象基类。

  • 这里metaclass=ABCMeta属于元编程的范围,想深入了解可以阅读Python学习笔记38:类元编程。

关于Python中类的更多内容,可以阅读《Python Cookbook》的相关章节。

本系列文章的代码都存放在Github项目:python-learning-notes。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: Python 类
最后更新:2021年6月8日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

取消回复

*

code

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号