本章前半部分,探索Python“一切皆对象”背后的含义;后半部分深入到对象相关的重要机制,如动态类型和垃圾回收。
1、一切皆对象
1)运算符
运算符是通过特殊方法实现的:
‘+’——‘.__add__()’——加法
‘*’——‘.__mul__()’——乘法
‘or’——‘.__or__()’——或者
运算的相关的特殊方法还能改变执行运算的方式:
①创建列表的子类——class SuberList(list):
②增加方法——def __sub__():——就是添加减法的定义
③使用方法——SuberList(list1)-SuberList(list2)
2)元素引用
表元素引用方式也是通过特殊方法实现的:
‘[0]’——‘.__getitem__(0)’——取位置为0的元素
‘[0]=1’——‘.__setitem__(0,1)’——将位置为0的元素替换为1
‘del dict['a]’——‘.__delitem__()’——将键a与他得值删去
3)内置函数的实现
内置函数也是调用对象的特殊方法:
‘len()’——‘.__len__()’——返回表中元素个数
‘abs()’——‘.__abs__()’——取绝对值
‘int()’——‘.__int__()’——取整数
2、属性管理
1)属性覆盖的背后
对象的属性来自对象属性和类属性,还可能从祖先类继承
调用属性——按分层管理顺序向下遍历,子类的属性比父类的同名属性有优先权,这是属性覆盖的关键
赋值属性——不按分层管理顺序,只搜索对象本身__dict__;如无,则增加
__dict__——返回对象的可写属性
__dir__——返回对象的相关属性
所以__dict__是dir()的子集,dir()包含__dict__中的属性
希望依赖于该属性的其他属性也同时变化,则需要即使生成属性的方法
2)特性
特性——是特殊的属性——使用内置函数property()创建
property()——前三个参数为函数,分别是设置获取,修改和删除特性
——最后参数为特性的文档,起说明作用
3)__getatte_()方法
__getattr_()——可以将即使生成的属性放在同一函数中处理,根据函数名区别处理不同属性(仅能用于查询不在__dict__的属性)
setattr()——可以对象添加或修改指定的属性(任意属性)
而delattr()——可以删除指定的对象属性(任意属性)
3、我是风儿,我是沙
1)动态数列
对象名是指向对象的引用,对象是存储在内存的实体
引用指向可以变更为不同类型,不同引用可以指向同一个对象
id()——返回对象的编号——可以查看引用指向哪个对象
2)可变与不可变对象
改变一个引用不会影响其他引用的指向,各个引用各自独立
可变对象——列表这种自身能发生改变的对象
不可变对象——赋值最多只能改变引用的指向
3)从动态数列看函数的参数传递
函数的参数传递本质传递的是引用
4、内存管理
1)引用管理
一个对象可以有多个引用,存有其被引用的总数
引用计数——sys包的getrefcount()——查看对象的引用计数
(传递给getrefcount()也算创建了引用,所以计数会比期望的多1)
2)对象引用对象
容器对象包含的并不是元素对象本身,而是指向各个元素对象的引用
所以容器对象引用元素对象时,元素对象引用计数会增加
globas()——可以查看全局引用关系的词典
容器对象引用会构成复杂拓扑结构
其中两个对象或者单个对象都可能为引用环,会给垃圾回收机制带来麻烦
objgraph包可绘制其引用关系
del,可以删除引用,使引用计数减少;也可删除容器中元素
3)垃圾回收
Python的某个对象引用计数降为0就会成为回收的垃圾
当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数,在当两者的差值高于某个阈值时,垃圾回收才会启动
gc模块中
get_threshold()——查看阈值,返回(启动阈值,回收1代触发值,回收2代触发值)
set_threshold()——设置阈值
gc.collect——手动启动
除了基础回收方式,还有分代回收策略:
Python将所有的对象分为0,1,2三代
所有的新建对象都是0代对象
当某一代对象经历过垃圾回收,依然存活,就被归入下一代
4)孤立的引用环
两个对象引用对方构成一个引用环,删除两个引用后,引用计数仍不为0,无法回收引用环
Python的回收机制:
①复制每个对象的引用计数,记为gc_ref_i
②对每个对象引用的对象j的引用次数减少1
③gc_ref不为0的对象和其引用的对象,以及继续更下游引用的对象被保留
④其余对象被垃圾回收