目錄
1.一切皆對象
2.屬性覆蓋機制
3.特殊屬性
4.__getattr__()方法
5.動態(tài)類型
6.內(nèi)存管理
1.一切皆對象
列表list的屬性中有一種特殊的方法__add__(),它可以使兩個list的對象相加,如[1, 2, 3]+[4, 5, 6]實質(zhì)是運行了[1, 2, 3]__add__([4, 5, 6]),結(jié)果為[1, 2, 3, 4, 5, 6]。類似的特殊方法還有:__mul__()表示乘法楞陷,__or__()表示or搏色。在這里需要注意温治,列表在python中不可以相減,但我們 可以通過__sub__()方法來添加減法操作的定義,如
除了列表的運算實質(zhì)是運用特殊的方法外肥照,列表中元素的引用也用到了特殊方法__getitem__(),如[1, 2, 3, 4, 5].__getitem__(3),就是從列表中引用第四個元素勤众。此外舆绎,關(guān)于列表或者字典元素的操作的方法還有
與運算符類似,許多內(nèi)置函數(shù)也都調(diào)用了對象的特殊方法们颜,比如__len__()吕朵、__abs__()、__int__()等窥突。
2.屬性覆蓋機制
之前在繼承中提到python的屬性覆蓋原理努溃,它實際上運用到了__dict__()方法,它是一個詞典阻问,鍵為屬性名梧税,對應(yīng)的值為某個屬性,看一個例子
輸出結(jié)果為
通過運用__dict__()方法可以看到称近,每個對象或者類中的屬性都儲存在一個字典中第队,如果我們用dir函數(shù)查看對象summe的屬性話,回發(fā)現(xiàn)summer的屬性分成了四層:summer/Chicken/Bird/object刨秆,當我們需要調(diào)用屬性的時候凳谦,python會一層層往下遍歷,知道找到那個屬性為止衡未,這也是屬性覆蓋的原理所在尸执。
注意,如果是對對象屬性進行賦值缓醋,那么python不會分層深入查找如失,以新的Chicken類的對象autumn為例
可以看到,對autumn的屬性賦值改衩,python在自身的__dict__()中找不到對應(yīng)的屬性就會直接在__dict__()中增加岖常,不會影響B(tài)ird類的屬性。
3.特殊屬性
如果屬性A會隨屬性B而變化葫督,那么屬性A就是一個特殊屬性(特性property)竭鞍,比如在Chicken類增加一個表示成年與否的特性adult板惑,當對象age大于1時,adult為真:
property()最多可以加載四個參數(shù)偎快,前三個參數(shù)為函數(shù)冯乘,分別用于設(shè)置獲取、修改和刪除特性晒夹,最后一個參數(shù)為特性的文檔裆馒,可以為一個字符串,起說明作用丐怯。再看一個例子
4.__getattr__()方法
在通過__dict__()方法無法調(diào)取對象屬性時喷好,python會調(diào)用__getattr__()方法來即時生成該屬性,如
需要注意的是读跷,__getattr__()只能用于查詢不在__dict__系統(tǒng)中的屬性梗搅。此外,__setattr__(self, name, value)和__delattr__(self, name)可用于修改和刪除屬性效览。它們的應(yīng)用面更廣无切,可用于任意屬性。
5.動態(tài)類型
動態(tài)類型(Dynamic Typing)是Python的另一個重要核心概念丐枉。變量的賦值就是動態(tài)類型的體現(xiàn)哆键。對a = 1,我們可以用id()函數(shù)查看對象的編號
在這里瘦锹,可以隨時變化的變量名就是可以變更指向的引用籍嘹,比如我們可以用b = a來變更指向,使得b也可以引用1沼本。這時噩峦,如果把a變更為a+2,那么a就變成指向3了抽兆,但是b還是指向1识补,如圖
可以看出,改變一個引用辫红,并不會影響其它引用的指向凭涂,也就是各個引用各自獨立,互不影響贴妻。但有種情況需要注意:
其實切油,這種情況的實際上表示的是我們通過list1[0]改變了列表里面的元素對象,并沒有改變列表對象名惩,list1和list2還是原來那個list1和list2澎胡。
函數(shù)中參數(shù)的傳遞也是一種引用的傳遞,當調(diào)用函數(shù)時,參數(shù)a傳遞給函數(shù)攻谁,函數(shù)中的變量x就指向了參數(shù)a所指的對象(相當于賦值)稚伍。如果參數(shù)是不可變對象,那么引用參數(shù)a和變量x直接相互獨立戚宦,互不影響个曙。如果傳遞的是可變對象,則不同受楼,如
6.內(nèi)存管理
語言的內(nèi)存管理是語言設(shè)計的一個重要方面垦搬。對象內(nèi)存管理是基于對引用的管理。我們可以用標準庫中sys包中的getrefcount()來查看某個對象的引用次數(shù)艳汽。因為參數(shù)傳遞實際上是創(chuàng)建了一個臨時的引用猴贰,所以getrefcount()的結(jié)果會比期望多1.如a = [1, 2, 3]中,a的引用計數(shù)是2骚灸,b = a糟趾,b的引用計數(shù)是3。如果是a = [1,2,3],b = [a, a],則a的引用計數(shù)是4甚牲。
兩個對象或者對象自身之間可以相互引用,這樣就構(gòu)成了引用環(huán)(Reference Cycle)蝶柿。引用環(huán)會給垃圾回收機制帶來很大麻煩丈钙。垃圾回收機制就是當無用的對象達到一定條件時,需要對無用的對象進行垃圾回收以釋放內(nèi)存交汤,其原理是某個對象的引用計數(shù)降為0雏赦,即沒有任何引用指向該對象時,它就成為了要被回收的垃圾芙扎。
在python運行時星岗,當分配對象(Object Allocation)和取消分配對象(Object Deallocation)的次數(shù)的差值高于某個閾值時,垃圾回收機制會啟動戒洼,可以通過gc模塊的get_threshold()方法俏橘,查看該閾值:
上面的700就是啟動回收機制的閾值,后面兩個10是與分代回收相關(guān)的閾值圈浇。所謂分代回收也是python回收的一種策略寥掐,python將所有對象按存活時間分為0,1磷蜀,2三代召耘,最新的對象為0代,0代的對象經(jīng)過一定次數(shù)的回收后褐隆,存活下來的對象會進入1代污它,python對0代和1代的對象進行回收,如此類推。這里的“一定次數(shù)”就是上圖的兩個10衫贬,表示每10次0代垃圾回收蜜宪,會配合1次1代的垃圾回收,而每10次1代的垃圾回收祥山,才會有1次2代的垃圾回收圃验。對于垃圾回收機制的閾值,我們可以用set_threshold()來調(diào)整缝呕。
上面還說到澳窑,引用環(huán)的垃圾回收比較復(fù)雜,因為組成引用環(huán)的兩個對象的引用計數(shù)都沒有降到0供常,python不會對它進行正常的回收摊聋。python的處理方法是:復(fù)制每個對象的引用計數(shù)(以2個對象a、b為例)栈暇,記為gc_ref_a和gc_ref_b麻裁,python開始遍歷所有對象,當遍歷到對象a引用b時源祈,相應(yīng)的gc_ref_b會減少1煎源,同理遍歷到b引用a時也一樣。這樣香缺,沒用的對象a和b最終的計數(shù)會變?yōu)?而被當成垃圾回收手销,其它未變成0的則繼續(xù)被python使用。