一 一切皆對(duì)象
1 運(yùn)算符
1.1.1運(yùn)算符中的特殊方法的操作
如果用dir(list)調(diào)查list的屬性激涤,可以看到一個(gè)屬性是_add_()這個(gè)特方法,它定義了“+”運(yùn)算符對(duì)于list對(duì)象的意義沐批。兩個(gè)list的對(duì)象相加時(shí),會(huì)進(jìn)行合并列表的操作。
>>>print([1,2,3]+[5,6,9]) #得到 [1,2,3,5,6,9]
運(yùn)算符,比如+兴溜、-、>耻陕、<、and刨沦、or的能都是通過特殊方法實(shí)現(xiàn)的诗宣。
>>>" abc "+ "xyz" #連接字符串,獲得“abcxyz”,實(shí)際上執(zhí)行了”abc._add_("xyz")的操作
兩個(gè)對(duì)象能否進(jìn)行加法運(yùn)算的關(guān)鍵就要看相應(yīng)的對(duì)象是否有_add_()方法想诅。
別的例子:
>>>(1.8)._mul_(2.0) #1.8*2.0
>>>True._or_(False) #True or False
1.2.1 運(yùn)算符中特殊方法的拓展操作
運(yùn)算相關(guān)的特殊方法還可以改變執(zhí)行運(yùn)算的方法召庞。
比如>>>[1,2,3]-[3,4] 是無法進(jìn)行減法操作的,因?yàn)榱斜頉]有定義“-”的運(yùn)算符来破,我們可以創(chuàng)建一個(gè)列表的子類篮灼,通過增加_sub_()的方法來增加減法操作的定義。
2 元素引用
2.1 _gtitem_()方法
下面時(shí)我們常見的表元素引用方法:
li=[1,2,3,4,5,6]
print(li[3]) #打印4
上述python發(fā)現(xiàn)并理解[]符號(hào)徘禁,然后調(diào)用_getitem_()方法
li=[1,2,3,4,5,6]
print(li._getitem_(3)) #打印4
3 內(nèi)置函數(shù)的實(shí)現(xiàn)
與運(yùn)算符類似诅诱,許多內(nèi)置函數(shù)也都是調(diào)用對(duì)象的特殊方法。
>>>len([1,2,3]) #返回表中元素的綜合
實(shí)際上是:
>>>[1,2,3]._len_()
別的例子:
>>>(-1).__abs__()
>>>(2.3).__int__()
二 屬性管理
1 屬性管理
1.1屬性管理的背后
為了深入理解屬性覆蓋送朱,我們有必要理解Python的__dict__屬性娘荡。當(dāng)我們調(diào)用調(diào)用對(duì)象的屬性時(shí),這個(gè)屬性可能來自對(duì)象屬性驶沼、類屬性炮沐、還有可能從祖先類那里繼承來的。一個(gè)類或?qū)ο髶碛械膶傩曰亓瑫?huì)記錄再__dict__中大年。這個(gè)__dict__時(shí)一個(gè)詞典,鍵為屬性名玉雾,對(duì)應(yīng)的值為某個(gè)屬性翔试。python在尋找對(duì)象的屬性時(shí),會(huì)按照繼承關(guān)系依次尋找__dict__
一個(gè)例子:
子類的屬性不父類的同名屬性有優(yōu)先權(quán)复旬,這正是屬性覆蓋的關(guān)鍵遏餐。
值得注意的是:上面都是調(diào)用屬性的操作,如果進(jìn)行賦值赢底,那么Python就不會(huì)分層深入查找了失都。
2 特性
同一個(gè)對(duì)象的不同屬性之間可能存在依賴關(guān)系柏蘑。 當(dāng)某個(gè)屬性被修改時(shí),我們希望以來與該屬性的其他屬性也同時(shí)變化粹庞。python提供了多種即時(shí)生成屬性的方法咳焚,其中一種稱為特性(property)。即特殊的屬性庞溜。
特殊屬性通過內(nèi)置函數(shù)property()來建立革半。property()最多可以加載四個(gè)參數(shù),前三個(gè)參數(shù)為函數(shù)流码,分別用于設(shè)置獲取又官、修改和刪除特性時(shí),Python應(yīng)該執(zhí)行的操作漫试。最后一個(gè)參數(shù)為特性的文檔六敬,可以為一個(gè)字符串起到說明作用。
一個(gè)例子:
3 .__getattr__()方法
除了內(nèi)置函數(shù)property外驾荣,我們還可以用__getattr__(slef,name)來查詢即時(shí)生成的屬性外构。
每個(gè)特性都需要有自己的處理函數(shù),而__getattr__()可以將所有的及時(shí)生成屬性都放在同一個(gè)函數(shù)集中處理播掷。需要特別注意的是审编,__getattr__()只能用于查詢不在__dict__系統(tǒng)中的屬性。
三 我是風(fēng)兒歧匈,我是沙
1 動(dòng)態(tài)類型(Dynamic Typing)
幾個(gè)概念:
對(duì)象名是只想這一對(duì)象的引用(reference)垒酬,對(duì)象是村粗在內(nèi)存中的實(shí)體
①通過內(nèi)置函數(shù)id(),我們能看到引用指向的是哪個(gè)對(duì)象:
②除了直接打印的id外,我們還可以用is運(yùn)算來判斷兩個(gè)引用是否指向同個(gè)對(duì)象件炉。
2 變與不可變對(duì)象
①一個(gè)對(duì)象可以有多個(gè)引用伤溉,看下面這個(gè)例子:#不可變對(duì)象的例子
對(duì)象沒變,只是引用變了妻率,從效果上看就是各個(gè)引用各自獨(dú)立乱顾,互不影響。這就是不可變對(duì)象宫静。
②列表自身能發(fā)生改變的對(duì)象走净,稱為可變對(duì)象(Mutable Object)
一個(gè)例子:
3 從動(dòng)態(tài)類型看函數(shù)的參數(shù)傳遞
函數(shù)的參數(shù)傳遞本質(zhì)上傳遞的是引用。
四 內(nèi)存管理
1 引用管理
對(duì)象內(nèi)存管理是基于對(duì)引用的管理孤里。在Python中伏伯,引用與對(duì)象分離。一個(gè)對(duì)象可以有多個(gè)引用捌袜,而每個(gè)對(duì)象都存有指向該對(duì)象的引用總數(shù)说搅,即引用計(jì)數(shù)(Reference Count)
我們可以i使用標(biāo)準(zhǔn)庫中sys包中的getrefcount(),來查看某個(gè)對(duì)象的引用計(jì)數(shù)虏等,需要注意的是弄唧,當(dāng)使用某個(gè)引用作為參數(shù)适肠,傳遞給getrefcount()時(shí),參數(shù)實(shí)際上時(shí)創(chuàng)建了一個(gè)臨時(shí)引用候引。因此侯养,getrefcount()所得到的結(jié)果會(huì)比期望多1.
2 對(duì)象引用對(duì)象
注意:當(dāng)一個(gè)對(duì)象a被另一個(gè)對(duì)象b引用,a的引用計(jì)數(shù)將增加1:
容器對(duì)象的引用可能會(huì)構(gòu)成很復(fù)雜的拓?fù)浣Y(jié)構(gòu):
x=[1,2,3]
y=[x,dict(key1=x)]
z=[y,(x,y)]
import objgraph
兩個(gè)對(duì)象可能相互引用澄干,從而構(gòu)成引用環(huán)(Reference Cycle)
a=[ ]
b=[a]
a.append(b)
即便是單個(gè)對(duì)象逛揩,只需要自己引用自己,也能構(gòu)成引用環(huán):
a=[ ]
a.append(a)
print(getrefcount(a))
3 垃圾回收(Garbage Collection)
3.1基礎(chǔ)回收
原理上麸俘,當(dāng)Python的某個(gè)對(duì)象的引用計(jì)數(shù)降為0了辩稽,即沒有任何引用指向?qū)ο髸r(shí),該對(duì)象就成為要被回收的垃圾了从媚。
如:
a=[1,2,3]
del a #刪除a之后逞泄,沒有任何引用指向[1,2,3],表[1,2,3]將被清除
Python指揮在特定的條件下静檬,自動(dòng)啟動(dòng)垃圾回收。當(dāng)Python運(yùn)行時(shí)并级,會(huì)記錄其中分配對(duì)象那個(gè)和取消分配對(duì)象的起初拂檩,當(dāng)兩者的差值高于某個(gè)閾值時(shí),垃圾回收才會(huì)啟動(dòng)嘲碧。
我們可以通過gc模塊的get_threshold()方法稻励,查看該閾值。
import gc
print(gc.get_threshold())
返回(700愈涩,10望抽,10)后面的兩個(gè)10時(shí)與分代回收相關(guān)的閾值。700是立即回收啟動(dòng)的閾值履婉。
3.2分代回收
python同時(shí)還采用了分代回收的策略煤篙。
python將所有對(duì)象分為0、1毁腿、2三代辑奈。所有新建對(duì)象都是0代對(duì)象。當(dāng)某一帶對(duì)象經(jīng)歷過垃圾回收已烤,依然存活鸠窗,那么它將被歸入下一代對(duì)象。垃圾回收啟動(dòng)時(shí)胯究,一定會(huì)掃描所有0代對(duì)象稍计。如果0代對(duì)象經(jīng)歷過一定次數(shù)的垃圾回收,那么就啟動(dòng)對(duì)0代和1代的掃描清理裕循。當(dāng)1代也經(jīng)歷了一定次數(shù)的垃圾回收后臣嚣,就啟動(dòng)對(duì)0净刮、1、2代的掃描茧球,即對(duì)所有對(duì)象進(jìn)行掃描庭瑰。
get_threshold()返回的(700,10抢埋,10)返回的兩個(gè)10弹灭,意思是,每10次0代垃圾回收揪垄,會(huì)配合1次1的樂基回收穷吮,而每10次1代回收,才會(huì)有1次2代的垃圾回收饥努。
可以采用set_threshold()來調(diào)整次數(shù)
import gc
gc.set_threshold(700,10,5)
4 孤立的引用環(huán)
這些引用環(huán)可能構(gòu)成無法使用捡鱼,但引用計(jì)數(shù)不為0的一些對(duì)象,會(huì)給垃圾回收機(jī)制帶來很大的困難酷愧。
為了回收這樣的引用環(huán)驾诈,Python會(huì)賦值每個(gè)對(duì)象的引用計(jì)數(shù),可以記作gc_ref溶浴。假設(shè)乍迄,每個(gè)對(duì)象i,該計(jì)數(shù)為gc_ref_i.Python會(huì)遍歷所有的對(duì)象i士败。對(duì)于每個(gè)對(duì)象i所引用的對(duì)象j闯两,將相應(yīng)的gc_ref_j減1.
在結(jié)束遍歷后,gc_ref不為0的對(duì)象和這邪惡對(duì)象引用的對(duì)象谅将,以及繼續(xù)更下游引用的對(duì)象漾狼,需要被保留,而其他對(duì)象則被垃圾回收饥臂。