类的定义
类的定义的语法
示例代码
类的实例化
- 创建对象使用
类名(__init__ 函数除第一个参数外的参数列表)
- 创建对象的时候实际执行了
__init__
函数 __init__
函数并不会创建对象
函数创建及初始化的过程
- 首先创建对象
- 对象作为self参数传递给
__init__
函数 - 返回self
作用域
类变量
示例代码
所以
- 类变量对类和实例都可见
- 所有实例共享类变量
- 当给实例的类变量赋值时,相当于动态的给这个实例增加了一个属性,覆盖了类变量
属性查找顺序
__dict__
: 实例变量的字典__class__
: 得到实例对应的类- 先查找
__dict__
在查找__class__
代码
类装饰器
参数是一个类,并且返回一个类的函数就可以是一个类装饰器。
类装饰器通常用于给类增加属性,如果增加方法,则都是类级的方法。
代码1:给类增加属性
函数方法增加:定义set_name函数给类F增加一个NAME属性
对set_name函数进行柯里化,实现带参数的类装饰器
代码2:给类增加方法
类装饰器get_name
给类H增加一个方法__get_name__
类方法/静态方法
方法的定义都是类级的,但是有的方法使用实例调用,有的方法使用类来调用
- 类方法:当一个方法,被classmethod装饰时, 第一个参数会变成类本身, 这样的方法叫类方法
- 当一个方法, 被staticmethod装饰的时候,不会自动传递第一个参数, 这样的方法叫静态方法
代码
- 实例方法只能由实例调用
- 类方法可以被类和实例使用,并且被实例使用时,传入的第一个参数还是类
- 静态方法可以被类和实例使用,都不会传入第一个参数
- 类中的普通方法,因为没有传入self,因此只能被类使用,实例无法使用
- 各种方法根据首参来决定。
访问控制
双下划线
- 所有双下划线开始,非双下划线结尾的成员,都是私有成员
- 严格的说, Python里没有真正私有成员
- Python的私有成员是通过改名实现的:_类名 + 带双下划綫的属性
- 除非真的有必要,并且清除明白的知道会有什么后果,否则不要通过改名规则修改私有成员
单下划线
- 单下划线是一种惯用法, 人为标记此成员为私有, 但是解释器不不做任何处理
property装饰器
引入property装饰器
当把number
属性变成私有属性__number
之后,无法直接访问得到,只能通过get_number
和set_number
两个函数访问__number
属性。
如果既能限制参数访问,又可以用类似属性这样简单的方式来访问类的变量,这个时候就可以使用property装饰器了。
- Python内置的
@property
装饰器就是负责把一个方法变成属性调用的
property装饰器使用
继承
单继承
- 在类名后加括号 括号中是继承列表, 称之为父类或者基类或者超类
- 继承一个明显的好处就是可以获取父类的属性和方法
- 凡是公有的都能继承
- 凡是私有的都不能继承
- 原来是什么,继承过来还是什么
方法重写
- 当子类和父类有同名成员的时候, 子类的成员会覆盖父类的同名成员
- 当父类含有一个带参数的初始化方法的时候,子类一定需要一个初始化方法,并且在初始化方法中调用父类的初始化方法
- super方法:super(type, obj) =》type:类名,obj:传递给后续方法的第一个参数
多继承与MRO
等效的类定义
多继承
- 在继承列表里存在多个类的时候表示多继承
- 多继承会把继承列表里的所有公有成员都继承过来
MRO
定义一个多继承,如下
如果定义类G继承自(A, E),如下
报错显示:Cannot create a consistent method resolution order (MRO) for bases E, A
方法解析顺序(MRO)不满足报错
分析基类E,A的MRO
所以,mro序列就是继承的先后顺序
那么G类的mro序列应该就是(G, A, E, object),Python通过C3算法来确定多继承的时候是否满足mro的两个原则:
- 本地优先: 自己定义或重写的方法优先,按照继承列表,从左到右查找
- 单调性:所有子类,也要满足查找顺序
C3算法的主要作用是:在多继承时判断属性来自于哪个类,无法判断时抛出TypeError
C3算法
merge操作就是C3算法的核心,merge步骤如下:
C3算法分析F类的mro
merge操作成功,mro解析正确,最终mro为[F, E, A, O]
C3算法分析G类的mro
第一个列表的首元素为A,在第二个列表中存在但不是首元素,不满足merge的条件,直接抛出异常。
结论:
- 应该尽量避免多继承
- 多继承会对程序的心智负担造成非常大的压力
Mixin类
参考
- 廖雪峰-多重继承与MixIn
- 知乎-Mixin是什么概念?
- Python Cookbook-利用Mixins扩展类功能
在编程中,mixin是指为继承自它的class提供额外的功能, 但它自身却是不单独使用的类.。在具有多继承能力的编程语言中, mixin可以为类增加额外功能或方法。
因此,MixIn模式的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。
在Python 3.5.2 源代码 socketserver.py 中的639到643行可以看到以下四个类的定义
- BaseServer:server类的基类
- UDPServer:UDP server class,继承自BaseServer
- TCPServer:TCP server class,继承自BaseServer
- ForkingMixIn:Mix-in class to handle each request in a new process.
- ThreadingMixIn:Mix-in class to handle each request in a new thread.
Python自带了TCPServer
和UDPServer
这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn
和ThreadingMixIn
提供。通过组合,就可以得到以上四个类。
这几个类之间的关系如下图:
可以看到,从BaseServer开始逐层继承的过程中,混入(MixIn)了ForkingMixIn类和ThreadingMixIn类。
这样的多重继承的技巧称为MixIn。
如果不采用MixIn技术,而是采用层次复杂的单继承实现,则类的数量会呈指数增长。
具体不采用MixIn技术设计的继承层次关系参见:廖雪峰-多重继承与MixIn中的Animal类的设计思路。
MixIn总结
MixIn其实也是一种组合的方式。通常来说,组合优于继承
Mixin 类的限制
- Mixin类不应该有初始化方法
- Mixin类通常不能独立工作
- Mixin类的祖先也应该是Mixin类
通常情况下,Mixin类总在继承列表的第一位