6.1 一切皆對(duì)象
6.2 屬性管理
6.3 我是風(fēng)兒,我是沙
6.4 內(nèi)存管理
6.1
.運(yùn)算符
涨缚。如果用dir(lisy)調(diào)查list的屬性,能看到一個(gè)屬性是_add_,從樣式上看,_add_是特殊方法策治,它特殊脓魏,在這個(gè)方法定義了"+"運(yùn)算符對(duì)于對(duì)象的意義兰吟,兩個(gè)list的對(duì)象相加時(shí)會(huì)進(jìn)行合并列表的操作。
print([1,2,3] + [5,6,9]) ? ? ?? #得到[1, 2, 3, 5, 6, 9]
print("abc" +"xyz") ? ? ? ? ? #連接字符串茂翔,獲得"abcxyz"
實(shí)際上執(zhí)行了如下的操作:
print("abc".__add__("xyz"))
運(yùn)算符混蔼,比如+,-珊燎,>惭嚣,<,and, or等都是通過特殊方法實(shí)現(xiàn)的悔政,比如:
__mul__ :*
__or__: or
??:列表在python中是不可以相減的晚吞,如下,則會(huì)顯示運(yùn)行錯(cuò)誤
我們可以創(chuàng)建一個(gè)列表的子類谋国,通過增加__sub__()方法槽地,來添加減法操作的定義
.元素的引用
常用方式
實(shí)際上程序運(yùn)行:
此外,還有其他方法旅急,比如:
__setitem__(x, y) ? #把下標(biāo)為x的元素?fù)Q為y
__delitem__(x) ? ? ? #刪除下標(biāo)為x的元素
__delitem__("x") ?? #對(duì)于詞典來說逢勾,刪除鍵為“a”的元素
.內(nèi)置函數(shù)的實(shí)現(xiàn)
與運(yùn)算符類似,許多內(nèi)置函數(shù)也都是調(diào)用對(duì)象的特殊方法藐吮。比如:
其它常用方法:
__abs__() ? #返回?cái)?shù)字的絕對(duì)值
__int__() ?? #取整數(shù)部分溺拱,往小取整
6.2 屬性管理
.屬性覆蓋的背后
。__dict__屬性: 一個(gè)類或?qū)ο髶碛械膶傩詴?huì)記錄在__dict__中谣辞,并以詞典形式表示出來迫摔,鍵為屬性名,值為對(duì)應(yīng)的屬性泥从。python在尋找對(duì)象的屬性時(shí)句占,會(huì)按照繼承關(guān)系依次尋找
例子:
輸出結(jié)果:
輸出順序是按照一下與summer對(duì)象的親近關(guān)系排列的,第一部分為summer對(duì)象自身的屬性躯嫉,也就是age纱烘。第二部分chicken類的屬性,比如fly和__init__()方法祈餐。第三部分為Bird類的屬性胳岂,比如feather诊沪,負(fù)責(zé)。最后一部分屬于object類器净,有諸如粘勒,__doc__之類的屬性
可以使用內(nèi)置函數(shù)dir來查看對(duì)象summer對(duì)象的屬性
對(duì)象的屬性是分層管理的,summer能接觸到的所有屬性,分別存在summer,Chicken,Bird,object這四層,然后我們需要調(diào)用某個(gè)屬性的時(shí)候至扰,Python會(huì)一層層向下遍歷,直到找到那個(gè)屬性塌碌,由于對(duì)象不需要重復(fù)存儲(chǔ)其祖先類的屬性渊胸,所以分層管理的機(jī)制可以節(jié)省存儲(chǔ)空間旬盯。
某個(gè)屬性可能在不同層被重復(fù)定義台妆,Python在向下遍歷的過程中,會(huì)選取先遇到的那一個(gè)胖翰。這也是屬性覆蓋的原理接剩,如果從summer調(diào)用chirp()方法,那么使用的將是和對(duì)象summer關(guān)系更近的Chicken的版本,而不是Bird的版本萨咳。即子類的屬性比父類的同名屬性有優(yōu)先權(quán)懊缺,這是屬性覆蓋的關(guān)鍵。
如果進(jìn)行賦值培他,那么python就不會(huì)分層深入查找了鹃两。比如:
盡管autumn修改了feather的屬性值,但他并沒有影響到Bird的類屬性舀凛。
也可以不依賴?yán)^承關(guān)系俊扳,直接去操作某個(gè)祖先類的屬性,比如:
實(shí)際上猛遍,相當(dāng)于修改Bird的__dict__: Bird.__dict__["feather"] = 3,但是直接進(jìn)行如下修改是不行的
同一個(gè)對(duì)象的不同屬性之間可能存在依賴關(guān)系馋记,當(dāng)某個(gè)屬性的修改,使我們希望依賴于該屬性的其他屬性也同時(shí)變化懊烤。Python提供了多種即時(shí)生產(chǎn)屬性的方法梯醒,其中一種稱為特性(property),特性使用內(nèi)置函數(shù)property()來創(chuàng)建腌紧,最多可以加載四個(gè)參數(shù)茸习,前三個(gè)參數(shù)為函數(shù),分別用于設(shè)置壁肋,獲取号胚,修改和刪除特性時(shí),Python應(yīng)該執(zhí)行的操作墩划,最后一個(gè)參數(shù)為特性的文檔涕刚,可以為一個(gè)字符串,起的是說明的作用乙帮,例子如下:
.__getattr__()方法
用__getattr__(self, name)來查詢即時(shí)生成的屬性杜漠。當(dāng)我們調(diào)用對(duì)象的一個(gè)屬性時(shí),如果通過__dict__機(jī)制,無法找到該屬性驾茴。那么python就會(huì)調(diào)用對(duì)象的__getattr__()方法,來即時(shí)生成該屬性盼樟。此外,即時(shí)生成屬性還有其他的方法锈至,比如使用descriptor類晨缴。
.6.3 我是風(fēng)兒,我是沙
峡捡。動(dòng)態(tài)類型击碗,python的變量不需要聲明,在賦值時(shí)们拙,變量可以重新復(fù)制為其他任意值稍途,這就是動(dòng)態(tài)類型的體現(xiàn)。
通過內(nèi)置函數(shù)id()砚婆,我們能查看到引用指向的是哪個(gè)對(duì)象械拍,這個(gè)函數(shù)能返回對(duì)象的編號(hào),也就是f返回對(duì)象的內(nèi)存地址装盯。
一個(gè)類可以有多個(gè)相等的對(duì)象坷虑,比如兩個(gè)長(zhǎng)字符可以是不同的對(duì)象,但他們的值可以相等埂奈。我們可以用is運(yùn)算來判斷兩個(gè)引用是否指向同一個(gè)對(duì)象
改變一個(gè)引用迄损,并不會(huì)影響其他引用的指向,從效果上看挥转,就是各個(gè)引用各自獨(dú)立海蔽,互不影響。如:
但注意以下情況:
在操作列表時(shí)党窜,如果通過元素引用改變了某個(gè)元素,那么列表對(duì)象自身會(huì)發(fā)生改變借宵,列表這種自身能發(fā)生改變的對(duì)象稱為可變對(duì)象(Mutable Object)幌衣,我們之前見過的詞典也是可變對(duì)象。但整數(shù)壤玫,浮點(diǎn)數(shù)和字符串豁护,則不能改變對(duì)象本身,賦值最多只能改變引用的指向欲间,這種對(duì)象稱為不可變對(duì)象楚里。
。從動(dòng)態(tài)類型看函數(shù)的參數(shù)傳遞
函數(shù)參數(shù)的傳遞猎贴,本質(zhì)上傳遞的是引用班缎,比如:
如果a是不可變對(duì)象蝴光,那么引用a和x之間相互獨(dú)立,即對(duì)參數(shù)x的操作并不會(huì)影響到引用a达址。
但如果傳遞的是可變對(duì)象蔑祟,情況就發(fā)生了變化,如下:
.內(nèi)存管理
我們可以使用標(biāo)準(zhǔn)庫中sys包中的getrefcount()疆虚,來查看某個(gè)對(duì)象的引用計(jì)數(shù)。需要注意的是满葛,當(dāng)把某一引用傳遞給getrefcount()時(shí)径簿,參數(shù)實(shí)際上是創(chuàng)建了一個(gè)臨時(shí)的引用,因此纱扭,getrefcount()所得到的結(jié)果會(huì)比期望的多1牍帚。
兩個(gè)對(duì)象可能相互引用儡遮,構(gòu)成引用環(huán)(Reference Cycle)
即使是單個(gè)對(duì)象乳蛾,只需自己引用自己,也能構(gòu)成引用環(huán)鄙币。
某個(gè)對(duì)象的引用計(jì)數(shù)可能減少肃叶,比如使用del關(guān)鍵字刪除某個(gè)引用,如果某個(gè)引用指向?qū)ο骯十嘿,那么當(dāng)這個(gè)引用被重新定向到其他某個(gè)對(duì)象時(shí)因惭,對(duì)象a的計(jì)數(shù)將減少
。垃圾回收
當(dāng)Python的某個(gè)對(duì)象的引用計(jì)數(shù)降為0绩衷,即沒有任何引用指向該對(duì)象時(shí)蹦魔,該對(duì)象就要成為被回收的垃圾了。
當(dāng)Python運(yùn)行時(shí)咳燕,會(huì)記錄其中分配對(duì)象和取消分配對(duì)象的次數(shù)勿决。當(dāng)兩者的差值高于某個(gè)閾值時(shí),垃圾回收才會(huì)啟動(dòng)招盲,可以通過gc模塊的get_threshold()方法低缩,查看該閾值
import gc
print(gc.get_threshold()) ? ? ? ? ? ? #返回(700, 10, 10)
返回(700, 10曹货, 10)咆繁,后面的兩個(gè)10是與分代回收相關(guān)的閾值,700即垃圾回收啟動(dòng)的閾值可以通過gc中的set_threshold()方法重新設(shè)置顶籽。當(dāng)然也可以手動(dòng)啟動(dòng)垃圾回收,即使用gc.collect()玩般。python除了基礎(chǔ)回收方式外,Python同時(shí)還采用了分代回收的策略礼饱。所以策略的基本假設(shè)是存活時(shí)間越久坏为,對(duì)象越不可能在后面的程序中變成垃圾设拟,對(duì)這樣一些“長(zhǎng)壽”對(duì)象,我們相信它們還有用處久脯,所以減少在垃圾回收中掃描它們的頻率纳胧。
Python將所有的對(duì)象分為0,1,2三代,所有的新建對(duì)象都是0代對(duì)象帘撰。當(dāng)某一代對(duì)象經(jīng)過垃圾回收依然存活跑慕,那它就會(huì)歸入下一代對(duì)象,垃圾回收啟動(dòng)時(shí)一定會(huì)掃描所有的0代對(duì)象摧找,如果0代經(jīng)過一定次數(shù)的垃圾回收核行,就會(huì)啟動(dòng)對(duì)0代和1代的掃描清理,當(dāng)1代也經(jīng)歷了一定次數(shù)的垃圾回收后蹬耘,就會(huì)啟動(dòng)對(duì)0芝雪,1,2代的掃描综苔,也就是對(duì)所有對(duì)象進(jìn)行掃描惩系。返回的(700, 10如筛, 10) 也就是說堡牡,每10次0代垃圾回收會(huì)配合1次1代的垃圾回收,而每10次1代圾回收杨刨,才會(huì)有1次2代的垃圾回收晤柄。同樣可以通過gc中的set_threshold()方法來調(diào)整次數(shù),比如對(duì)2代對(duì)象進(jìn)行更頻繁的掃描妖胀,如:
import gc
gc.set_threshold(700,10,5) ? ? ? ? ? ? ? ? ? #調(diào)整次數(shù)芥颈,對(duì)2代對(duì)象進(jìn)行更頻繁的掃描
print(gc.get_threshold())
。孤立的引用環(huán)
引用環(huán)的存在給上面的垃圾回收機(jī)制帶來很大的困難赚抡,這些引用環(huán)可能構(gòu)成無法使用爬坑,但上面的計(jì)數(shù)不為0的一些對(duì)象:
a = []
b = [a]
a.append(b)
del a
del b
刪了a,b引用之后,這兩個(gè)對(duì)象不可能再?gòu)某绦蛑姓{(diào)用怕品,也就沒有什么用處妇垢,但是由于引用環(huán)的存在,兩個(gè)對(duì)象的引用計(jì)數(shù)都沒有降到零肉康,所以不會(huì)被垃圾回收闯估。
為了回收引用環(huán),Python會(huì)復(fù)制每個(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ì)象則被垃圾回收莽红。
代碼地址:https://gitee.com/mabingqi/learn_programming_from_python/tree/master/