變量視作便利貼
賦值:把變量分配給對象卵沉。先有對象
Python 變量類似 Java 中的引用式變量
標(biāo)識
對象一旦創(chuàng)建,它的標(biāo)識絕不會更改
可以把標(biāo)識理解為內(nèi)存地址法牲,is 運(yùn)算符比較兩對象的標(biāo)識史汗;id() 返回對象的標(biāo)識的整數(shù)表示
is 與 ==
- == 比較的是值,is 比較的是兩個整數(shù) id
- is 比 == 快拒垃,因?yàn)?is 不能重載停撞,而 == 背后是 a._eq_(b),object 的 _eq_ 比較的是兩對象的 id悼瓮,與 is 一致戈毒。
但多數(shù)內(nèi)置類型覆蓋了此方法,考慮對象的屬性的值横堡,為此做相等性測試可能涉及大量處理工作埋市。 - 變量與單例值比較應(yīng)使用 is。 x is None命贴;x is not None
tuple 的相對不可變性
- 容器序列保存的是對象的引用
單一數(shù)據(jù)類型的扁平序列在連續(xù)內(nèi)存中保存的是數(shù)據(jù)本身 -
tuple 不可變指 tuple 的物理內(nèi)容(保存的引用)不可變道宅。與引用的對象無關(guān)食听!
區(qū)分賦值與 append
默認(rèn)做淺復(fù)制
-
淺復(fù)制:只復(fù)制最外層容器,副本中的元素是源容器元素的引用
使用構(gòu)造方法或 [ : ] 實(shí)現(xiàn)
淺復(fù)制 - 對于可變對象元素污茵,由于存放的是引用樱报,復(fù)制的也是引用。一個列表改變泞当,另外一個列表也改變迹蛤。
- 對于不可變對象元素,雖然存放的是引用襟士,復(fù)制的也是引用笤受。
但只要發(fā)生改變,實(shí)質(zhì)是創(chuàng)建了新對象敌蜂,原來的對象還是不會變。自然不會影響到另外一個列表
深復(fù)制
深復(fù)制:副本【與源本不共享內(nèi)部對象】copy.deepcopy(object)
淺復(fù)制:copy.copy(object)津肛,背后分別是 _copy_()章喉、_deepcopy_()
參數(shù)傳遞
Python 使用的是共享傳參(call by sharing),即形參是實(shí)參的別名
- x 是不可變對象身坐,當(dāng) x += y 時秸脱,實(shí)質(zhì)創(chuàng)建了新對象,且新對象的作用域只在函數(shù)內(nèi)部蛇,原來的 x 沒變摊唇。
- p 是可變對象,當(dāng) p += q 時涯鲁,p 已經(jīng)變了巷查。
不要使用可變對象作為函數(shù)默認(rèn)值
-
默認(rèn)值在定義函數(shù)時計(jì)算(通常在加載模塊時,也就是導(dǎo)入模塊時)抹腿,即 passen=[] 只在加載模塊時執(zhí)行一次岛请,
因此默認(rèn)值變成了函數(shù)這個對象的屬性。
如果默認(rèn)值是可變對象警绩,而且修改了它的值崇败,后續(xù)函數(shù)調(diào)用都會受到影響。
默認(rèn)值 - 所以通常使用 None 作為接受可變值參數(shù)的默認(rèn)值
防御可變參數(shù)
def __init__(self, passengers=None):
if passengers is None:
passen = []
else:
self.passengers = list(passengers)
- 用 None 作為接受可變值參數(shù)的默認(rèn)值
- 如果直接 self.passengers = passengers 還是不夠妥當(dāng)肩祥,因?yàn)檫@樣形參和實(shí)參共享同一個對象后室,形參變了,實(shí)參也會發(fā)生改變混狠。list(passengers) 相當(dāng)淺復(fù)制了 passengers岸霹。反正一句話,不要直接對形參進(jìn)行操作檀蹋,否則會影響實(shí)參松申。應(yīng)該生成形參副本再操作云芦。
del 和垃圾回收
引用計(jì)數(shù):
CPython 垃圾回收算法。當(dāng)對象的引用歸零時贸桶,CPython 會在對象上調(diào)用_del_ 方法(前提是定義了)舅逸,然后釋放分配給對象的內(nèi)存。
分代垃圾回收
CPython 2.0 增加的算法皇筛。
如果一組對象全是相互引用琉历,如: a = [2, 3], b= [a, 5], a.append(b)
即使再出色的引用方式也會導(dǎo)致組中對象不可獲取。
- weakref.finalize(s1, bye):在 s1 引用的對象上注冊 bye 回調(diào)函數(shù)
弱引用
- 上例中旗笔,finalize 持有 {1, 2, 3} 的弱引用
- 在「緩存」中,經(jīng)常要引用對象拄踪,卻不讓對象存在時間超過所需時間
-
弱引用不會增加對象引用數(shù)量蝇恶,弱引用引用的對象稱為「所指對象 referent」。弱引用不會影響 referent 被當(dāng)作垃圾回收
弱引用 - wref() 會返回被引用對象惶桐,因?yàn)檫@是控制臺會話撮弧,返回的對象會綁定到 _ 變量
- 即使刪除了引用 a,仍有引用 _ 綁定對象 {0, 1}
-
當(dāng)使用 wref() is None 時姚糊,_ 會綁定返回值 False贿衍,這時對象{0, 1} 就沒有引用了
_ 自動綁定 wref() 返回對象
WeakValueDictionary
- weakref.ref 類是底層接口(少用),較常用的是 finalize 和 weakref 集合(WeakKeyDictionary救恨、WeakValueDictionary贸辈、WeakSet)
- WeakValueDictionary 類實(shí)現(xiàn)的是可變映射,
里面的值是對象的弱引用肠槽,被引用的對象被回收后擎淤,
對應(yīng)的鍵自動從 WeakValueDictionary 中刪除。
常用作「緩存」
WeakValueDictionary - stock 是WeakValueDictionary 的一個實(shí)例署浩,值是對象的弱引用揉燃。
- del catalog 意味著被引用的對象被回收,按道理來說筋栋,stock 的鍵也會自動刪除炊汤,但是最后一個鍵被保留了。
- 這時因?yàn)?for 循化中的 cheese 是全局變量弊攘,循環(huán)結(jié)束后綁定著對象 Cheese('ccc')抢腐,所以del catalog 后,對象 Cheese('ccc') 仍有引用 cheese襟交,所以不被當(dāng)作垃圾回收迈倍,直至 del cheese 后才被當(dāng)作垃圾回收,對應(yīng)的鍵也就自動刪除捣域。
weakSet
保存元素弱引用的集合類啼染,當(dāng)元素沒有強(qiáng)引用時宴合,自動刪除該元素。
對不可變對象的優(yōu)化
- 使用一個元組創(chuàng)建另外一個元組(tuple()迹鹅、[:]卦洽、copy、deepcopy)斜棚,
得到的是同一個對象阀蒂。 - 共享字符串字面量是一種優(yōu)化措施,稱為「駐留 interning」
- 類似的還有 bytes弟蚀、frozenset 實(shí)例蚤霞、較小的整數(shù),這樣能節(jié)省內(nèi)存义钉,提高解釋器速度昧绣。其實(shí)不了解也無傷大雅。
雜談
- 從 object 繼承的_eq_ 方法(即 == 運(yùn)算符)比較的是對象 id
- 用戶創(chuàng)建的類捶闸,其實(shí)例默認(rèn)可變
- 可變對象是導(dǎo)致多線程編程難以處理的主要原因滞乙。某個線程改動對象后,不正確同步則損壞數(shù)據(jù)鉴嗤,過度同步又導(dǎo)致死鎖。
- Python 沒有手動銷毀對象的機(jī)制序调。這是個好特性:如果能手動銷毀對象醉锅,那么指向?qū)ο蟮膹?qiáng)引用就不知怎么處理了。
- CPython 中发绢,這樣寫是安全的:
open('test.txt', 'wt', encoding='utf-8').write('1, 2, 3')
因?yàn)槲募ο蟮囊脭?shù)量在 write 方法返回后歸零硬耍,銷毀內(nèi)存中文件對象之前,會立即關(guān)閉文件边酒。而在 Jpython 或 IronPython 中经柴,open().write() 卻是不安全的,因?yàn)樗鼈儾灰揽恳糜?jì)數(shù)墩朦。