一 一切皆对象
1 运算符
1.1.1运算符中的特殊方法的操作
如果用dir(list)调查list的属性,可以看到一个属性是_add_()这个特方法,它定义了“+”运算符对于list对象的意义。两个list的对象相加时,会进行合并列表的操作。
>>>print([1,2,3]+[5,6,9]) #得到 [1,2,3,5,6,9]
运算符,比如+、-、>、<、and、or的能都是通过特殊方法实现的。
>>>" abc "+ "xyz" #连接字符串,获得“abcxyz”,实际上执行了”abc._add_("xyz")的操作
两个对象能否进行加法运算的关键就要看相应的对象是否有_add_()方法。
别的例子:
>>>(1.8)._mul_(2.0) #1.8*2.0
>>>True._or_(False) #True or False
1.2.1 运算符中特殊方法的拓展操作
运算相关的特殊方法还可以改变执行运算的方法。
比如>>>[1,2,3]-[3,4] 是无法进行减法操作的,因为列表没有定义“-”的运算符,我们可以创建一个列表的子类,通过增加_sub_()的方法来增加减法操作的定义。
2 元素引用
2.1 _gtitem_()方法
下面时我们常见的表元素引用方法:
li=[1,2,3,4,5,6]
print(li[3]) #打印4
上述python发现并理解[]符号,然后调用_getitem_()方法
li=[1,2,3,4,5,6]
print(li._getitem_(3)) #打印4
3 内置函数的实现
与运算符类似,许多内置函数也都是调用对象的特殊方法。
>>>len([1,2,3]) #返回表中元素的综合
实际上是:
>>>[1,2,3]._len_()
别的例子:
>>>(-1).__abs__()
>>>(2.3).__int__()
二 属性管理
1 属性管理
1.1属性管理的背后
为了深入理解属性覆盖,我们有必要理解Python的__dict__属性。当我们调用调用对象的属性时,这个属性可能来自对象属性、类属性、还有可能从祖先类那里继承来的。一个类或对象拥有的属性,会记录再__dict__中。这个__dict__时一个词典,键为属性名,对应的值为某个属性。python在寻找对象的属性时,会按照继承关系依次寻找__dict__
一个例子:
子类的属性不父类的同名属性有优先权,这正是属性覆盖的关键。
值得注意的是:上面都是调用属性的操作,如果进行赋值,那么Python就不会分层深入查找了。
2 特性
同一个对象的不同属性之间可能存在依赖关系。 当某个属性被修改时,我们希望以来与该属性的其他属性也同时变化。python提供了多种即时生成属性的方法,其中一种称为特性(property)。即特殊的属性。
特殊属性通过内置函数property()来建立。property()最多可以加载四个参数,前三个参数为函数,分别用于设置获取、修改和删除特性时,Python应该执行的操作。最后一个参数为特性的文档,可以为一个字符串起到说明作用。
一个例子:
3 .__getattr__()方法
除了内置函数property外,我们还可以用__getattr__(slef,name)来查询即时生成的属性。
每个特性都需要有自己的处理函数,而__getattr__()可以将所有的及时生成属性都放在同一个函数集中处理。需要特别注意的是,__getattr__()只能用于查询不在__dict__系统中的属性。
三 我是风儿,我是沙
1 动态类型(Dynamic Typing)
几个概念:
对象名是只想这一对象的引用(reference),对象是村粗在内存中的实体
①通过内置函数id(),我们能看到引用指向的是哪个对象:
返回的编号相同②除了直接打印的id外,我们还可以用is运算来判断两个引用是否指向同个对象。
2 变与不可变对象
①一个对象可以有多个引用,看下面这个例子:#不可变对象的例子
对象没变,只是引用变了,从效果上看就是各个引用各自独立,互不影响。这就是不可变对象。
②列表自身能发生改变的对象,称为可变对象(Mutable Object)
一个例子:
3 从动态类型看函数的参数传递
函数的参数传递本质上传递的是引用。
不可变对象 可变对象
四 内存管理
1 引用管理
对象内存管理是基于对引用的管理。在Python中,引用与对象分离。一个对象可以有多个引用,而每个对象都存有指向该对象的引用总数,即引用计数(Reference Count)
我们可以i使用标准库中sys包中的getrefcount(),来查看某个对象的引用计数,需要注意的是,当使用某个引用作为参数,传递给getrefcount()时,参数实际上时创建了一个临时引用。因此,getrefcount()所得到的结果会比期望多1.
2 对象引用对象
对象引用对象的例子
注意:当一个对象a被另一个对象b引用,a的引用计数将增加1:
容器对象的引用可能会构成很复杂的拓扑结构:
x=[1,2,3]
y=[x,dict(key1=x)]
z=[y,(x,y)]
import objgraph
两个对象可能相互引用,从而构成引用环(Reference Cycle)
a=[ ]
b=[a]
a.append(b)
即便是单个对象,只需要自己引用自己,也能构成引用环:
a=[ ]
a.append(a)
print(getrefcount(a))
3 垃圾回收(Garbage Collection)
3.1基础回收
原理上,当Python的某个对象的引用计数降为0了,即没有任何引用指向对象时,该对象就成为要被回收的垃圾了。
如:
a=[1,2,3]
del a #删除a之后,没有任何引用指向[1,2,3],表[1,2,3]将被清除
Python指挥在特定的条件下,自动启动垃圾回收。当Python运行时,会记录其中分配对象那个和取消分配对象的起初,当两者的差值高于某个阈值时,垃圾回收才会启动。
我们可以通过gc模块的get_threshold()方法,查看该阈值。
import gc
print(gc.get_threshold())
返回(700,10,10)后面的两个10时与分代回收相关的阈值。700是立即回收启动的阈值。
3.2分代回收
python同时还采用了分代回收的策略。
python将所有对象分为0、1、2三代。所有新建对象都是0代对象。当某一带对象经历过垃圾回收,依然存活,那么它将被归入下一代对象。垃圾回收启动时,一定会扫描所有0代对象。如果0代对象经历过一定次数的垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,就启动对0、1、2代的扫描,即对所有对象进行扫描。
get_threshold()返回的(700,10,10)返回的两个10,意思是,每10次0代垃圾回收,会配合1次1的乐基回收,而每10次1代回收,才会有1次2代的垃圾回收。
可以采用set_threshold()来调整次数
import gc
gc.set_threshold(700,10,5)
4 孤立的引用环
这些引用环可能构成无法使用,但引用计数不为0的一些对象,会给垃圾回收机制带来很大的困难。
为了回收这样的引用环,Python会赋值每个对象的引用计数,可以记作gc_ref。假设,每个对象i,该计数为gc_ref_i.Python会遍历所有的对象i。对于每个对象i所引用的对象j,将相应的gc_ref_j减1.
在结束遍历后,gc_ref不为0的对象和这邪恶对象引用的对象,以及继续更下游引用的对象,需要被保留,而其他对象则被垃圾回收。
网友评论