第六章 與對(duì)象的深入交往

與對(duì)象的深入交往

6.1 一切皆對(duì)象

1. 運(yùn)算符

運(yùn)算符是可以定義的贝室,運(yùn)算符有如:+、-仿吞、<滑频、>、and茫藏、or等误趴。
我們用特殊方法__add__()來定義加法__sub__()來定義減法务傲,__mul__()來定義乘法凉当,__or__()來定義的邏輯運(yùn)算......
定義運(yùn)算符,對(duì)復(fù)雜對(duì)象的處理極其有幫助售葡,例如某種物體的屬性多樣看杭,若只取其中一種屬性進(jìn)行比較而決定物體的好壞,那么物體之間的比較就可以 定義成 其中的一種屬性的比較挟伙。

2.元素引用

如同運(yùn)算符一般楼雹,元素引用的許多形式或函數(shù)都是通過調(diào)用特殊方法來實(shí)現(xiàn)的。有如:

  1. li[3]→li.__getitem__(3)
  2. li.insert(3,0)→li.__setitem__(3,0)
  3. dict.pop("a")→dict.__delitem__("a")
    ......
3. 內(nèi)置函數(shù)的實(shí)現(xiàn)

直接列舉一些:
1.len([1,2,3])→[1,2,3].__len__()
2.abs(-1)→(-1).__abs__()
3.int(2.3)→(2.3).__int__()
......

6.2 屬性管理

1.屬性覆蓋

以前我們學(xué)到了繼承,其中提及到屬性覆蓋贮缅,但未進(jìn)行深究榨咐,此刻便稍微對(duì)繼承中的屬性覆蓋進(jìn)行更為深刻的了解。
在理解屬性覆蓋之前谴供,我們需要知道的是Python的__dict__屬性块茁。當(dāng)一個(gè)類或者對(duì)象擁有屬性時(shí),便會(huì)記錄在字典__dict__中桂肌。其中鍵為屬性名数焊,值為對(duì)應(yīng)的屬性值。Python在尋找對(duì)象的屬性時(shí)崎场,會(huì)依照繼承關(guān)系來依次查找__dict__佩耳。
首先,類Object是類層次結(jié)構(gòu)的根類谭跨,Object類是所有類的父類干厚,所以放在最底層,然后到定義的一個(gè)大類即父類(相對(duì)其屬下)饺蚊,父類又擁有子類萍诱,最后到屬于子類的對(duì)象。顯然這是一個(gè)分層管理機(jī)制污呼。當(dāng)我們要調(diào)用某個(gè)屬性時(shí)裕坊,會(huì)采取就近原則,即從對(duì)象往底層搜尋字典__dict__燕酷。正因如此籍凝,某個(gè)屬性可能在不同的層被重復(fù)定義過,于是在向下歷遍的過程中苗缩,取最近的便會(huì)發(fā)生屬性覆蓋饵蒂。

這是屬性覆蓋的原理所在。

查看屬性:

對(duì)象名/類名.__dict__

調(diào)用屬性時(shí)酱讶,Python會(huì)分層查找退盯;但是如果是賦值時(shí),Python只會(huì)在該層進(jìn)行操作泻肯,在__dict__中添加或修改屬性渊迁。
也可以不考慮繼承,直接修改父類的屬性灶挟。

2. 特性

即時(shí)生成屬性的方法之一——特性(property)琉朽,即特殊的屬性。目的是希望修改屬性時(shí)稚铣,依賴其的其他屬性也能同時(shí)變化箱叁。
內(nèi)置函數(shù)property()創(chuàng)建特性:

class num(object):
    def __init__(self,value):
        self.value = value
    def get_neg(self):
        return -self.value
    def set_neg(self,value):
        self.value = -value
    def del_neg(self):
        print("value also deleted")
        del self.value
    neg = property(get_neg,set_neg,del_neg,"I'm negative")   
#property函數(shù)最多可以加載4個(gè)參數(shù)墅垮,分別用于設(shè)置獲取、修改和刪除特性耕漱,最后一個(gè)為說明文檔

x = num(1.1)
print(x.neg)
x.neg = -22
print(x.value)
print(num.neg.__doc__)
del x.neg
3. __getattr__方法

除了內(nèi)置函數(shù)property來查詢即時(shí)生成的屬性算色,還可以用__getattr__(self,name)方法。當(dāng)__dict無法找到要調(diào)用的屬性時(shí)螟够,就會(huì)調(diào)用__getattr__()來即時(shí)生成該屬性剃允。(__getattr__()只能用來查詢__dict__不存在的屬性,__getattribute__()則可以查詢?nèi)我鈱傩?/strong>)
示例:

class Bird(object):
    feather = True
class chicken(Bird):
    fly = False
    def __init__(self,age):
         self.age = age
    def __getattr__(self,name):
        if name == "adult":
            if self.age > 1.0:
                return True
            else:
                return False
        else:
            raise AttributeError(name)

summer = chicken(2)
print(summer.adult)              #True

summer.age = 0.5
print(summer.adult)             #False
print(summer.male)              #拋出錯(cuò)誤AttributeError

__getattr__()可以將所有即時(shí)生成的屬性集中在一個(gè)函數(shù)內(nèi)進(jìn)行處理齐鲤,其根據(jù)函數(shù)名區(qū)別處理不同的屬性
另外:__setattr__(self,name,value)和__delattr__(self,name)分別用于修改和刪除屬性椒楣,可用于任意屬性给郊。
Python描述符 (descriptor) 詳解
對(duì)象屬性的訪問順序:
① __getattribute__(), 無條件調(diào)用
② 數(shù)據(jù)描述符:由 ① 觸發(fā)調(diào)用 (若人為的重載了該 __getattribute__() 方法捧灰,可能會(huì)調(diào)職無法調(diào)用描述符)
③ 實(shí)例對(duì)象的字典(若與描述符對(duì)象同名淆九,會(huì)被覆蓋哦)
④ 類的字典
⑤ 非數(shù)據(jù)描述符
⑥ 父類的字典
⑦ __getattr__() 方法

6.3 我是風(fēng)兒,我是沙

1. 動(dòng)態(tài)類型

典型的動(dòng)態(tài)類型的體現(xiàn)就是變量毛俏,變量不需要事先聲明炭庙,其可以任意多次賦值以改變它的值。來一個(gè)簡(jiǎn)單的語(yǔ)句:

a = 1

a是對(duì)象名煌寇,1是對(duì)象焕蹄,對(duì)象名是指向?qū)ο蟮囊茫褪?strong>對(duì)象名相當(dāng)于中介阀溶,因?yàn)閷?duì)象是存儲(chǔ)在內(nèi)存中的實(shí)體腻脏,我們碰不到它,所以要一個(gè)中間人去接觸它银锻。
通過內(nèi)置函數(shù)id()我們可以查看引用指向的是哪個(gè)對(duì)象永品。
如此,重新賦值就是換個(gè)對(duì)象而已击纬,中介不變鼎姐。即變量名是個(gè)隨時(shí)變更指向的引用,自然更振,類型也可以"隨變"炕桨。
Python會(huì)緩存小的整數(shù)和段字符串,可以通過is運(yùn)算來判斷引用之間是否指向同一對(duì)象殃饿。

2. 可變與不可變對(duì)象

一個(gè)對(duì)象可有多個(gè)引用谋作。

引用之間呈獨(dú)立性,即改變一個(gè)引用乎芳,不會(huì)影響其他引用的指向遵蚜。
對(duì)于以下情況:

list2 = [1,2,3]
list1 = list2
list1[0] = 10
print(list2)

并非失去了引用的獨(dú)立性帖池,而是list1和list2依舊指向同一列表,但是list1[0] = 10改變的正是指向的列表中的第一位元素的指向吭净,即只改變了其中一個(gè)元素的指向睡汹,因此指向該列表的對(duì)象名(引用)都會(huì)受到影響。
可變對(duì)象:如列表寂殉、詞典這種自身能發(fā)生改變的對(duì)象囚巴。
不可變對(duì)象:如整數(shù)、浮點(diǎn)數(shù)友扰、字符串彤叉、元組這種不能改變對(duì)象本身,賦值最多只能改變引用的指向的不可變數(shù)據(jù)對(duì)象村怪。

3. 從動(dòng)態(tài)類型看函數(shù)的參數(shù)傳遞

函數(shù)的參數(shù)傳遞秽浇,本質(zhì)上傳遞的是引用。

def nishama(x):
    print(id(x))
    x = 100
    print(id(x))

a = 1
print(id(a))

nishama(a)
print(a)

這里是不可變對(duì)象的操作甚负,其實(shí)就是賦值操作柬焕。

def nishama(x):
    x[0] = 10
    print(x)

a = [1,2,3]
nishama(a)
print(a)        #打印[10,2,3]

這是可變對(duì)象的操作,通過引用操作可變對(duì)象會(huì)影響到其他的引用梭域。

6.4 內(nèi)存管理

1. 引用管理

一個(gè)對(duì)象可以有多個(gè)引用斑举,引用次數(shù)就是來記錄這些引用總數(shù)的〔≌牵可以用標(biāo)準(zhǔn)庫(kù)中sys包中的getrefcount()來查看某個(gè)對(duì)象的引用次數(shù)富玷。
當(dāng)使用某個(gè)引用為參數(shù),傳遞給getrefcount()時(shí)既穆,參數(shù)實(shí)際上是創(chuàng)建了一個(gè)臨時(shí)的引用凌彬。故getrefcount()所得到的結(jié)果會(huì)比期望多1:

from sys import getrefcount
a = [1,2,3]
print(getrefcount(a))    #打印2,即比期望多1循衰,1+1=2

b = a
print(getrefcount(b))    #打印3铲敛,2+1=3
2. 對(duì)象引用對(duì)象

對(duì)于可變對(duì)象,如列表和詞典会钝,對(duì)象是包含對(duì)象的伐蒋。準(zhǔn)確地說,容器對(duì)象包含的是指向各個(gè)元素對(duì)象的引用迁酸。
若在主程序中使用a = 1先鱼,就會(huì)把這個(gè)關(guān)系存入到一個(gè)詞典中,這個(gè)詞典是記錄所有全局引用的奸鬓,可以用globals()來查看這個(gè)詞典焙畔。
當(dāng)一個(gè)對(duì)象a被另外一個(gè)對(duì)象b引用時(shí),a的引用計(jì)數(shù)將增加1:

from sys import getrefcount
a = [1,2,3]
print(getrefcount(a))        #打印2
b = [a,a]
print(getrefcount(a))        #打印4串远,1+1+2=4宏多,引用2次a所以增加2

容器對(duì)象的引用可能會(huì)構(gòu)成很復(fù)雜的拓?fù)浣Y(jié)構(gòu)(其實(shí)可能就是花樣套娃)儿惫,于是可以用objgraph包來描繪其引用關(guān)系:

x = [1,2,3]
y = [x,dict(key1=x)]
z = [y,(x,y)]

import objgraph
objgraph.show_refs([z],filename='ref_topo.png')   #第二個(gè)參數(shù)說明繪圖文件名

兩個(gè)對(duì)象相互引用,形成引用環(huán)伸但。
del關(guān)鍵字:1肾请、刪除某個(gè)引用:del a
2、刪除容器中的元素:del a[0]
引用的轉(zhuǎn)移會(huì)使對(duì)象的引用次數(shù)減少更胖,就是有一個(gè)中介人不做你的生意了铛铁,你的收入就少一份。

3. 垃圾回收

Python的垃圾回收機(jī)制即對(duì)于沒有引用次數(shù)的對(duì)象却妨,會(huì)進(jìn)行刪除回收饵逐,將其所占內(nèi)存清除
垃圾回收的啟動(dòng)機(jī)制:會(huì)設(shè)置一個(gè)關(guān)于分配對(duì)象和取消分配對(duì)象的次數(shù)差值的閾值彪标。一旦高于這個(gè)閾值梳毙,垃圾回收才會(huì)啟動(dòng)【柘拢可以通過gc模塊的get_threshold()方法來查看閾值或改變閾值。(會(huì)返回三個(gè)值萌业,第一個(gè)就是回收啟動(dòng)的閾值坷襟,后面兩個(gè)值是與分代回收相關(guān)的閾值)
手動(dòng)啟動(dòng)回收:使用gc.collect()
上面講的是基礎(chǔ)回收,下面講分代回收生年。
分代回收將對(duì)象分為0婴程、1、2三代抱婉,新建的是0代档叔,經(jīng)歷過一次垃圾回收仍留下的對(duì)象就歸為1代,如果再經(jīng)歷過一次回收依舊留下的對(duì)象就歸為2代蒸绩。垃圾回收啟動(dòng)時(shí)衙四,肯定對(duì)0代全部對(duì)象進(jìn)行掃描。若0代經(jīng)過一定次數(shù)的垃圾回收患亿,就對(duì)0代和1代進(jìn)行掃描清理传蹈。1代也經(jīng)歷了一定次數(shù)的回收,就對(duì)0步藕、1惦界、2代進(jìn)行掃描,全部對(duì)象的掃描咙冗。
前面提到的后兩個(gè)值就是(每n次0代垃圾回收會(huì)配合1次1代的垃圾回收)沾歪,(每n次1代就會(huì)有1次2代清查)

4. 孤立的引用環(huán)

還記得上上一小節(jié)講的引用環(huán)嗎雾消?它對(duì)垃圾回收機(jī)制帶來很大的麻煩灾搏。簡(jiǎn)而言之挫望,就是引用環(huán)鎖住了一些對(duì)象,使一些對(duì)象的引用次數(shù)不降為0确镊,但是這些對(duì)象又偏偏被刪除了引用士骤。
為了這樣的引用環(huán),Python出了一個(gè)針對(duì)性解決的方法:就是復(fù)制每個(gè)對(duì)象的引用次數(shù)蕾域,記作gc_ref拷肌,假設(shè)每個(gè)對(duì)象i,計(jì)數(shù)就為gc_ref_i旨巷。Python歷遍所有的對(duì)象i巨缘,對(duì)于每個(gè)對(duì)象所引用的對(duì)象j,將相應(yīng)的gc_ref_j減去1采呐,當(dāng)歷遍結(jié)束以后若锁,保留gc_ref不為0的對(duì)象和這些對(duì)象引用的對(duì)象及更下游引用的對(duì)象(一個(gè)套娃有效,它的里面全部套娃就有效)斧吐,其他的進(jìn)行回收又固。

總結(jié):Python是一種動(dòng)態(tài)類型的語(yǔ)言,其對(duì)象與引用分離煤率,內(nèi)置垃圾回收以釋放內(nèi)存仰冠,因是以引用次數(shù)為依據(jù)的簡(jiǎn)單垃圾回收機(jī)制,所以需要對(duì)孤立引用環(huán)的問題進(jìn)行解決蝶糯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末洋只,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昼捍,更是在濱河造成了極大的恐慌识虚,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妒茬,死亡現(xiàn)場(chǎng)離奇詭異担锤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乍钻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門妻献,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人团赁,你說我怎么就攤上這事育拨。” “怎么了欢摄?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵熬丧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我怀挠,道長(zhǎng)析蝴,這世上最難降的妖魔是什么害捕? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮闷畸,結(jié)果婚禮上尝盼,老公的妹妹穿的比我還像新娘。我一直安慰自己佑菩,他們只是感情好盾沫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著殿漠,像睡著了一般赴精。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绞幌,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天蕾哟,我揣著相機(jī)與錄音,去河邊找鬼莲蜘。 笑死谭确,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的票渠。 我是一名探鬼主播逐哈,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼庄新!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起薯鼠,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤择诈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后出皇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羞芍,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年郊艘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荷科。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纱注,死狀恐怖畏浆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狞贱,我是刑警寧澤刻获,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站瞎嬉,受9級(jí)特大地震影響蝎毡,放射性物質(zhì)發(fā)生泄漏厚柳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一沐兵、第九天 我趴在偏房一處隱蔽的房頂上張望别垮。 院中可真熱鬧,春花似錦扎谎、人聲如沸碳想。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)移袍。三九已至,卻和暖如春老充,著一層夾襖步出監(jiān)牢的瞬間葡盗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工啡浊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留觅够,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓巷嚣,卻偏偏與公主長(zhǎng)得像喘先,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子廷粒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 6.1 一切皆對(duì)象 6.2 屬性管理 6.3 我是風(fēng)兒窘拯,我是沙 6.4 內(nèi)存管理 6.1 .運(yùn)算符 。如果用dir...
    mAbbQi閱讀 359評(píng)論 0 0
  • 6.1 一切皆對(duì)象 6.1.1 運(yùn)算符 兩個(gè)對(duì)象是否能進(jìn)行運(yùn)算坝茎,首先就要看相應(yīng)的對(duì)象是否有特殊方法涤姊。若對(duì)象沒有此方...
    SibyLtuI閱讀 290評(píng)論 0 0
  • 6.1一切皆對(duì)象 1.運(yùn)算符 兩個(gè)語(yǔ)法能否進(jìn)行加法運(yùn)算,首先就要看相應(yīng)的對(duì)象是否有__add__()方法嗤放。 列表對(duì)...
    lammmya閱讀 151評(píng)論 0 0
  • 6.1一切皆對(duì)象 6.2屬性管理 6.3我是風(fēng)兒思喊,我是沙 6.4內(nèi)存管理 6.11運(yùn)算符 兩個(gè)對(duì)象能否可以運(yùn)算,關(guān)...
    HXXHXX閱讀 228評(píng)論 0 0
  • 本章前半部分次酌,探索Python“一切皆對(duì)象”背后的含義恨课;后半部分深入到對(duì)象相關(guān)的重要機(jī)制,如動(dòng)態(tài)類型和垃圾回收岳服。 ...
    Guodw閱讀 196評(píng)論 0 0