摘要:函數(shù)的參數(shù)作為引用時(shí)唯一支持的參數(shù)傳遞模式是共享傳參,它指函數(shù)的形參獲得實(shí)參中各個(gè)引用的副本,即形參是實(shí)參的別名。而在上面這個(gè)例子中,類的屬性實(shí)際上是形參所指向的對(duì)象所指對(duì)象,的別名。
《流暢的Python》筆記1. 變量、標(biāo)識(shí)、相等性和別名本篇是“面向?qū)ο髴T用方法”的第一篇,一共六篇。本篇主要是一些概念性的討論,內(nèi)容有:Python中的變量,對(duì)象標(biāo)識(shí),值,別名,元組的某些特性,深淺復(fù)制,引用,函數(shù)參數(shù),垃圾回收,del命令,弱引用等,比較枯燥,但卻能解決程序中不易察覺(jué)的bug。
先用一個(gè)形象的比喻來(lái)說(shuō)明Python中的變量:變量是標(biāo)注而不是盒子。也就是說(shuō),Python中的變量更像C++中的引用,最能說(shuō)明這一點(diǎn)的就是多個(gè)變量指向同一個(gè)列表,但也有例外,在遇到某些內(nèi)置類型,比如字符串str時(shí),變量則變成了“盒子”:
# 代碼1 >>> a = [1, 2] >>> b = a # 標(biāo)注,引用 >>> a.append(3) >>> b [1, 2, 3] >>> c = "c" >>> d = c # “盒子” >>> c = "cc" >>> d "c"
補(bǔ)充:說(shuō)到了賦值方式,Python和C++一樣,也是等號(hào)右邊先執(zhí)行。
1.1 相等性( == )與標(biāo)識(shí)( is )用一個(gè)更學(xué)術(shù)的詞來(lái)替換“標(biāo)注”,那就是“別名”。在C++中,引用就是變量的別名,Python中也是,比如代碼1中的變量b就是變量a的別名,但如果是以下形式,變量b則不是a的別名:
# 代碼2 >>> a = [1, 2] >>> b = [1, 2] >>> a == b # a和b的值相等 True >>> a is b # a和b分別綁定了不同的對(duì)象,雖然對(duì)象的值相等 False
==檢測(cè)對(duì)象的值是否相等,is運(yùn)算符檢測(cè)對(duì)象的標(biāo)識(shí)(ID)是否相等,id()返回對(duì)象標(biāo)識(shí)的整數(shù)表示。一般判斷兩對(duì)象的標(biāo)識(shí)是否相等并不直接使用id(),更多的是使用is運(yùn)算符。
對(duì)象ID在不同的實(shí)現(xiàn)中有所不同:在CPython中,id()返回對(duì)象的內(nèi)存地址,但在其他Python解釋器中可能是別的值。但不管怎么,對(duì)象的ID一定唯一,且在生命周期中保持不變。
通常我們關(guān)心的是值,而不是標(biāo)識(shí),所以==出現(xiàn)的頻率比is高。但在變量和單例值之間比較時(shí),應(yīng)該使用is。目前,最常使用is檢測(cè)變量綁定的值是不是None,推薦的寫法是:
# 代碼3 x is None # 并非 x == None x is not None # 并非 x != None
is運(yùn)算符比==速度快,因?yàn)樗荒苤剌d,所以Python不用尋找并調(diào)用特殊方法,而是直接比較兩個(gè)對(duì)象的ID。a == b其實(shí)是語(yǔ)法糖,實(shí)際調(diào)用a.__eq__(b)。雖然繼承自object的__eq__方法也是比較對(duì)象的ID,結(jié)果和is一樣,但大多數(shù)內(nèi)置類型覆蓋了該方法,處理過(guò)程更復(fù)雜,這就是為什么is比==快。
1.2 元組的相對(duì)不可變性元組和大多數(shù)Python集合一樣,保存的是對(duì)象的引用。元組的不可變性其實(shí)是指tuple數(shù)據(jù)結(jié)構(gòu)的物理內(nèi)容(即保存的引用)不可變,與引用的對(duì)象無(wú)關(guān)。如果引用的對(duì)象可變,即便元組本身不可變,元素依然可變,不變的是元素的標(biāo)識(shí):
# 代碼4 >>> t1 = (1, 2, [30, 40]) >>> t2 = (1, 2, [30, 40]) >>> t1 == t2 True >>> id(t1[-1]) 2019589413704 >>> t1[-1].append(99) >>> t1 (1, 2, [30, 40, 99]) >>> id(t1[-1]) # 內(nèi)容變了,標(biāo)識(shí)沒(méi)有變 2019589413704 >>> t1 == t2 False
這同時(shí)也說(shuō)明,并不是每個(gè)元組都是可散列的!
2.深淺復(fù)制復(fù)制對(duì)象時(shí),相等性和標(biāo)識(shí)之間的區(qū)別有更深入的影響。副本與源對(duì)象相等,但I(xiàn)D不同。而如果對(duì)象內(nèi)部還有其他對(duì)象,這就涉及到了深淺復(fù)制的問(wèn)題:到底是復(fù)制內(nèi)部對(duì)象呢還是共享內(nèi)部對(duì)象?
2.1 默認(rèn)做淺復(fù)制對(duì)列表和其他可變序列來(lái)說(shuō),我們可以使用構(gòu)造方法或[:]來(lái)創(chuàng)建副本。然而,這兩種方法做的都是淺復(fù)制,它們只復(fù)制了最外層的容器,副本中的元素是源容器中元素的引用。如果所有元素都是不可變的,那這樣做沒(méi)問(wèn)題,還能節(jié)省內(nèi)存;但如果其中有可變?cè)兀@么做就可能出問(wèn)題:
# 代碼5 l1 = [3, [11, 22], (7, 8)] l2 = list(l1) # <1> l1.append(100) l1[1].remove(22) print("l1:", l1, " l2:", l2) l2[1] += [33, 44] # <2> l2[2] += (10, 11) # <3> print("l1:", l1, " l2:", l2) # 結(jié)果 l1: [3, [11], (7, 8), 100] # 追加元素只影響了l1 l2: [3, [11], (7, 8)] # 但刪除l1[1]中的元素影響了兩個(gè)列表 l1: [3, [11, 33, 44], (7, 8), 100] # +=對(duì)可變對(duì)象是就地操作,影響了兩個(gè)列表 l2: [3, [11, 33, 44], (7, 8, 10, 11)] # +=對(duì)不可變對(duì)象會(huì)創(chuàng)建新對(duì)象,只影響了l2
以上代碼有3點(diǎn)需要解釋:
<1>:l1[1]和l2[1]指向同一列表,l1[2]和l2[2]指向同一元組。因?yàn)槭菧\復(fù)制,只是復(fù)制引用;
<2>:+=運(yùn)算對(duì)可變對(duì)象來(lái)說(shuō)是就地運(yùn)算,不會(huì)創(chuàng)建新對(duì)象,所以對(duì)兩個(gè)列表都有影響;
<3>:+=運(yùn)算對(duì)元組這樣的不可變對(duì)象來(lái)說(shuō),等同于l2[2] = l2[2] + (10, 11),此操作隱式地創(chuàng)建了新對(duì)象,l2[2]重新綁定到了新對(duì)象,所以只有列表l2[2]發(fā)生了改變,而l1[2]沒(méi)有改變。
2.2 為任意對(duì)象做深復(fù)制和淺復(fù)制淺復(fù)制并非是一種錯(cuò)誤,只是一種選擇。而有時(shí)我們需要的是深復(fù)制,即副本不共享內(nèi)部對(duì)象的引用。copy模塊提供的deepcopy和copy函數(shù)能為任意對(duì)象做深復(fù)制和淺復(fù)制。
# 代碼6 import copy l1 = [3, [11, 22]] l2 = copy.copy(l1) # 淺復(fù)制 l3 = copy.deepcopy(l1) # 深復(fù)制 l1[1].append(33) # 影響了l2,但沒(méi)有影響l3 print("l1:", l1, " l2:", l2, " l3:", l3) # 結(jié)果 l1: [3, [11, 22, 33]] l2: [3, [11, 22, 33]] l3: [3, [11, 22]]
在做深復(fù)制時(shí),如果對(duì)象之間有循環(huán)引用,樸素的深復(fù)制算法(換句話說(shuō)就是你自己寫的深復(fù)制算法)很可能會(huì)陷入無(wú)限循環(huán),然后報(bào)錯(cuò)。deepcopy會(huì)記住已經(jīng)復(fù)制的對(duì)象,而不會(huì)進(jìn)入無(wú)限循環(huán):
# 代碼7 >>> a = [10, 20] >>> b = [a, 30] # 包含a的引用 >>> b [[10, 20], 30] >>> a.append(b) # 相互引用 >>> a [10, 20, [[...], 30]] >>> a[2][0] [10, 20, [[...], 30]] >>> a[2][0][2][0] [10, 20, [[...], 30]] >>> from copy import deepcopy >>> c = deepcopy(a) # 不會(huì)報(bào)錯(cuò),能正確處理相互引用的問(wèn)題 >>> c [10, 20, [[...], 30]]
此外,深復(fù)制有時(shí)可能太深了。例如,對(duì)象可能會(huì)引用不該復(fù)制的外部資源或單例值,這時(shí),深復(fù)制就不應(yīng)該復(fù)制這些值。如果要控制copy和deepcopy的行為,我們可以在對(duì)象中重寫特殊方法__copy__和__deepcopy__,具體內(nèi)容這里就不展開(kāi)了,大家可以參考copy模塊的官方文檔。
3. 函數(shù)參數(shù)通過(guò)別名共享對(duì)象還能解釋Python中傳遞參數(shù)的方式,以及使用可變類型作為參數(shù)默認(rèn)值引起的問(wèn)題。
3.1 函數(shù)的參數(shù)作為引用時(shí)Python唯一支持的參數(shù)傳遞模式是共享傳參(call by sharing),它指函數(shù)的形參獲得實(shí)參中各個(gè)引用的副本,即形參是實(shí)參的別名。這種方案的結(jié)果就是,函數(shù)可能會(huì)修改作為參數(shù)傳入的可變對(duì)象,但無(wú)法修改這些對(duì)象的標(biāo)識(shí)(不能把一個(gè)對(duì)象替換成另一個(gè)對(duì)象):
# 代碼8 def f(a, b): a += b return a x, y = 1, 2 print(f(x, y), x, y) a, b = [1, 2], [3, 4] print(f(a, b), a, b) t, u = (10, 20), (30, 40) print(f(t, u), t, u) # 結(jié)果 3 1 2 # x, y是不可變對(duì)象,沒(méi)有影響到x, y [1, 2, 3, 4] [1, 2, 3, 4] [3, 4] # x是可變對(duì)象,影響到了x (10, 20, 30, 40) (10, 20) (30, 40) # x沒(méi)有指向新的元組,但形參a指向了新的元組3.2 參數(shù)默認(rèn)值
不要使用可變類型作為參數(shù)的默認(rèn)值!其實(shí)這個(gè)問(wèn)題在之前的文章“Python學(xué)習(xí)之路7-函數(shù)”的2.3小節(jié)中有所提及?,F(xiàn)在我們來(lái)看下面這個(gè)例子:
首先定義一個(gè)類:
# 代碼9 class Bus: def __init__(self, passengers=[]): # 默認(rèn)值是個(gè)可變對(duì)象 self.passengers = passengers def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name)
下面是這個(gè)類的行為:
# 代碼10 >>> bus1 = Bus(["Alice", "Bill"]) # 直到第8行Bus的表現(xiàn)都是正常的 >>> bus1.passengers ["Alice", "Bill"] >>> bus1.pick("Charlie") >>> bus1.drop("Alice") >>> bus1.passengers ["Bill", "Charlie"] >>> bus2 = Bus() # 使用默認(rèn)值 >>> bus2.pick("Carrie") >>> bus2.passengers ["Carrie"] # 到目前為止也是正常的 >>> bus3 = Bus() # 也是用默認(rèn)值 >>> bus3.passengers ["Carrie"] # 不正常了! >>> bus3.pick("Dave") >>> bus2.passengers ["Carrie", "Dave"] # bus2的值也被改變了 >>> bus2.passengers is bus3.passengers # 這倆是同一對(duì)象的別名 True >>> bus1.passengers # bus1依然正常 ["Bill", "Charlie"]
上述行為的原因在于,參數(shù)的默認(rèn)值在導(dǎo)入模塊時(shí)計(jì)算,方法或函數(shù)的形參指向這個(gè)默認(rèn)值。而在上面這個(gè)例子中,類的屬性self.passengers實(shí)際上是形參passengers所指向的對(duì)象(所指對(duì)象,referent)的別名。而bus1行為正常是因?yàn)閺囊婚_(kāi)始它的passengers就沒(méi)有指向默認(rèn)值。
這里有點(diǎn)像單例模式:參數(shù)的默認(rèn)值是唯一的,只要采用默認(rèn)值,不管創(chuàng)建多少個(gè)Bus的實(shí)例,它們的self.passengers都是同一個(gè)空列表[]對(duì)象的別名,不會(huì)為每一個(gè)實(shí)例多帶帶創(chuàng)建一個(gè)專屬的[]。
運(yùn)行上述代碼之后,可以查看Bus.__init__對(duì)象的__defaults__屬性,它存儲(chǔ)了參數(shù)的默認(rèn)值:
# 代碼11 >>> Bus.__init__.__defaults__ (["Carrie", "Dave"],) >>> Bus.__init__.__defaults__[0] is bus2.passengers # self.passengers就是一個(gè)別名! True
這也說(shuō)明了為什么要用None作為接收可變值的參數(shù)的默認(rèn)值:
# 代碼12 class Bus: def __init__(self, passengers=None): # 默認(rèn)值是個(gè)可變對(duì)象 if passengers is None: # 并不推薦 if passengers == None 這種寫法 self.passengers = [] else: self.passengers = list(passengers) # 注意這里! -- snip --
代碼12中的第7行并不是直接把形參passengers賦值給self.passengers,而是形參的副本(這里是淺復(fù)制)。如果直接賦值,即self.passengers = passengers(self.passengers變成了用戶傳入的參數(shù)的別名),則用戶傳入的參數(shù)在運(yùn)行過(guò)程中可能會(huì)被修改,而這并不一定是用戶想要的,這便違反了"最少驚訝原則"(居然還真有這么個(gè)原則)
4. del和垃圾回收對(duì)象絕不會(huì)自行銷毀;然而,無(wú)法得到對(duì)象時(shí),可能會(huì)被當(dāng)做垃圾回收。——Python語(yǔ)言參考手冊(cè)
del語(yǔ)句刪除變量(即"引用"),而不是對(duì)象。del命令可能導(dǎo)致對(duì)象被當(dāng)做垃圾回收,但這僅發(fā)生在當(dāng)刪除的變量保存的是對(duì)象的最后一個(gè)引用,或者無(wú)法得到對(duì)象時(shí)(如果兩個(gè)對(duì)象相互引用,如代碼7,當(dāng)它們的引用只存在二者之間時(shí),垃圾回收程序會(huì)判定它們都無(wú)法獲取,進(jìn)而把它們都銷毀)。重新綁定也可能會(huì)導(dǎo)致對(duì)象的引用數(shù)量歸零,進(jìn)而對(duì)象被銷毀。
在CPython中,垃圾回收使用的主要算法是引用計(jì)數(shù)。實(shí)際上,每個(gè)對(duì)象都會(huì)統(tǒng)計(jì)有多少個(gè)引用指向自己。當(dāng)引用計(jì)數(shù)歸零時(shí),對(duì)象立即被銷毀。但在其他Python解釋器中則不一定是引用計(jì)數(shù)算法。
補(bǔ)充:有個(gè)__del__特殊方法,它不是用來(lái)銷毀實(shí)例的,而是在實(shí)例被銷毀前用來(lái)執(zhí)行一些最后的操作,比如釋放外部資源等。我們不應(yīng)該在代碼中調(diào)用它,Python解釋器會(huì)在銷毀實(shí)例時(shí)先調(diào)用它(如果定義了),然后再釋放內(nèi)存。它相當(dāng)于C++中的析構(gòu)函數(shù)。
我們可以使用weakref.finalize來(lái)演示對(duì)象被銷毀時(shí)的情況:
# 代碼13 >>> import weakref >>> s1 = {1, 2, 3} >>> s2 = s1 >>> def bye(): # 它充當(dāng)一個(gè)回調(diào)函數(shù) ... print("Gone with the wind...") # 一定不要傳入待銷毀對(duì)象的綁定方法,否則會(huì)有一個(gè)指向?qū)ο蟮囊?>>> ender = weakref.finalize(s1, bye) # 在s1引用的對(duì)象上注冊(cè)bye回調(diào) >>> ender.alive True >>> del s1 >>> ender.alive True # 說(shuō)明 del s1并沒(méi)有刪除對(duì)象 >>> s2 = "spam" Gone with the wind... # 引用計(jì)數(shù)為零,對(duì)象被刪除 >>> ender.alive False5. 弱引用
不知道大家看到上述代碼第15行時(shí)會(huì)不會(huì)產(chǎn)生如下疑惑:第8行代碼明明把s1引用傳給了finalize函數(shù)(為了監(jiān)控對(duì)象和調(diào)用回調(diào),必須要有引用),那么對(duì)象{1, 2, 3}則應(yīng)該至少有三個(gè)引用,可為什么最后它還是被銷毀了呢?這就牽扯到了弱引用這個(gè)概念。
5.1 weakref.ref弱引用不會(huì)妨礙所指對(duì)象被當(dāng)做垃圾回收,即弱引用不會(huì)增加對(duì)象的引用計(jì)數(shù)。(弱引用常被用于緩存,但具體用在緩存的哪些地方目前筆者還不清楚.....)
弱引用還是可調(diào)用對(duì)象,下面的代碼展示了如何使用weakref.ref實(shí)例獲取所指對(duì)象。
補(bǔ)充在代碼之前:Python控制臺(tái)會(huì)自動(dòng)把結(jié)果不為None的表達(dá)式的結(jié)果綁定到變量_(下劃線)上。這也說(shuō)明了一個(gè)問(wèn)題:微觀管理內(nèi)存時(shí),隱式賦值會(huì)為對(duì)象創(chuàng)建新引用,而這有可能會(huì)導(dǎo)致一些意外結(jié)果。
# 代碼14 >>> import weakref >>> a_set = {1, 2} # 對(duì)象{1, 2}的引用數(shù)+1 >>> wref = weakref.ref(a_set) # 并沒(méi)有增加所指對(duì)象的引用數(shù) >>> wref5.2 weakref集合>>> wref() # 弱引用是個(gè)可調(diào)用對(duì)象 {1, 2} # 發(fā)生了隱式賦值,變量 _ 指向了對(duì)象{1, 2},引用數(shù)+1 >>> a_set = {2, 3} # 引用數(shù) -1 >>> wref() # 所指對(duì)象依然存在,還沒(méi)有被銷毀 {1, 2} >>> wref() is None # 此時(shí)所指對(duì)象依然存在 False # 變量 _ 指向了對(duì)象False,對(duì)象{1, 2}引用數(shù)歸零,銷毀 >>> wref() is None # 驗(yàn)證所指對(duì)象已被銷毀 True
weakref.ref類其實(shí)是底層接口,供高級(jí)用途使用,一般程序最好使用werakref集合和finalize函數(shù),即最好使用WeakKeyDictionary、WeakValueDictionary、WeakSet和finalize(它們?cè)趦?nèi)部使用弱引用),不推薦自己動(dòng)手創(chuàng)建并處理weakref.ref實(shí)例,除非你的工作就是專門和這些東西打交道的。
WeakValueDictionary類實(shí)現(xiàn)的是一種可變映射,里面的值("鍵值對(duì)"中的"值",而不是字典中的"值")是對(duì)象的弱引用。被引用的對(duì)象在程序中的其他地方被當(dāng)做垃圾回收后,對(duì)應(yīng)的鍵會(huì)自動(dòng)從WeakValueDictionary中刪除。因此,它經(jīng)常用于緩存。(查看緩存中變量是否依然存在?給框架用?)
# 代碼15 >>> import weakref >>> class Cheese: ... def __init__(self, kind): ... self.kind = kind ... >>> stock = weakref.WeakValueDictionary() >>> catalog = [Cheese("Red Leicester"), Cheese("Parmesan")] >>> for cheese in catalog: ... stock[cheese.kind] = cheese ... >>> sorted(stock.keys()) ["Red Leicester", "Parmesan"] # 表現(xiàn)正常 >>> del catalog >>> sorted(stock.keys()) ["Parmesan"] # 這是怎么回事? >>> del cheese # 這是問(wèn)題所在 >>> sorted(stock.keys()) []
臨時(shí)變量引用了對(duì)象,這可能會(huì)導(dǎo)致該變量的存在時(shí)間比預(yù)期長(zhǎng)。通常,這對(duì)局部變量來(lái)說(shuō)不是問(wèn)題,因?yàn)樗鼈冊(cè)诤瘮?shù)返回時(shí)會(huì)被銷毀。但上述代碼中,for循環(huán)中的變量cheese是全局變量,除非顯示刪除,否則不會(huì)消失。
與WeakValueDictionary對(duì)應(yīng)的是WeakKeyDictionary,后者的鍵是弱引用,它的一些可能用途如下:
它的實(shí)例可以為應(yīng)用中其他部分擁有的對(duì)象附加數(shù)據(jù),這樣就無(wú)需為對(duì)象添加屬性。這對(duì)屬性訪問(wèn)受限的對(duì)象尤其有用。
WeakSet類的用途則很簡(jiǎn)單:"保存元素弱引用的集合。當(dāng)某元素沒(méi)有強(qiáng)引用時(shí),集合會(huì)把它刪除。"如果一個(gè)類需要知道它的所有實(shí)例,一種好的方案是創(chuàng)建一個(gè)WeakSet類型的類屬性,保存實(shí)例的弱引用。
5.3 弱引用的局限weakref集合以及一般的弱引用,能處理的對(duì)象類型有限:
基本的list和dict實(shí)例不能作為弱引用的所指對(duì)象,但它們的子類則可以;
class MyList(list): """MyList的實(shí)例可作為弱引用的所指對(duì)象"""
set的實(shí)例可作為所指對(duì)象;
自定義類的實(shí)例可以;
int和tuple的實(shí)例不能作為弱引用的所指對(duì)象,它們的子類也不行。
但這些局限基本上是CPython的實(shí)現(xiàn)細(xì)節(jié),其他Python解釋器的情況可能不同。
6. CPython對(duì)不可變類型走的捷徑本節(jié)內(nèi)容是Python實(shí)現(xiàn)的細(xì)節(jié),可以跳過(guò)。
這些細(xì)節(jié)是CPython核心開(kāi)發(fā)者走的捷徑和優(yōu)化措施,利用這些細(xì)節(jié)寫的代碼在其他Python解釋器中可能沒(méi)用,在CPython未來(lái)的版本中也可能沒(méi)用。下面是具體內(nèi)容:
對(duì)元組t來(lái)說(shuō),t[:]和tuple(t)不創(chuàng)建副本,而是返回同一個(gè)對(duì)象的引用;
str、bytes和frozenset實(shí)例也是如此,并且frozenset的copy方法返回的也不是副本(注意,frozenset的實(shí)例fs不能用fs[:],因?yàn)?b>fs不是序列);
str的實(shí)例還有共享字符串字面量的行為:
>>> s1 = "ABC" >>> s2 = "ABC" >>> s1 is s2 True
這叫做"駐留"(interning),這是一種優(yōu)化措施。CPython還會(huì)在小的整數(shù)上使用這種優(yōu)化,防止重復(fù)創(chuàng)建常用數(shù)字,如0,-1。但CPython不會(huì)駐留所有字符串和數(shù)字,駐留的條件是實(shí)現(xiàn)細(xì)節(jié),而且沒(méi)有文檔說(shuō)明。所以千萬(wàn)不要依賴這個(gè)特性?。ū容^字符串或數(shù)字請(qǐng)用==,而不是is?。?/p>
7. 總結(jié)每個(gè)Python對(duì)象都有標(biāo)識(shí)、類型和值,只有對(duì)象的值可能變化。
變量保存的是引用,這對(duì)Python編程有很多實(shí)際的影響:
簡(jiǎn)單的賦值不會(huì)創(chuàng)建副本;
對(duì)+=或*=等運(yùn)算符來(lái)說(shuō),如果左邊的變量綁定了不可變對(duì)象,則會(huì)創(chuàng)建新對(duì)象,然后重新綁定;如果是可變對(duì)象,則就地修改;
對(duì)現(xiàn)有的變量賦予新值不會(huì)修改之前綁定的對(duì)象。這叫重新綁定:現(xiàn)有變量綁定了其它對(duì)象。如果變量是之前那個(gè)對(duì)象的最后一個(gè)引用,該對(duì)象會(huì)被回收;
函數(shù)的參數(shù)以別名的形式傳遞,這意味著,函數(shù)可能會(huì)修改通過(guò)參數(shù)傳入的可變對(duì)象。這一行為無(wú)法避免,除非在函數(shù)內(nèi)部創(chuàng)建副本,或者使用不可變對(duì)象;
不要使用可變類型作為函數(shù)的默認(rèn)值!
==用于比較值,is用于比較引用。
某些情況下,可能需要保存對(duì)象的引用,但不留存對(duì)象本身,比如記錄某個(gè)類的所有實(shí)例,這可以用弱引用解決。
迎大家關(guān)注我的微信公眾號(hào)"代碼港" & 個(gè)人網(wǎng)站 www.vpointer.net ~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/41836.html
摘要:一對(duì)象引用基礎(chǔ)知識(shí)變量是標(biāo)注而不是容器。也就是說(shuō)元組中不可變的是元素的標(biāo)識(shí),但元組的值會(huì)隨著引用的可變對(duì)象變化而變化。在中每個(gè)對(duì)象的引用都會(huì)有統(tǒng)計(jì)。弱引用不會(huì)妨礙對(duì)象被當(dāng)做垃圾回收。 導(dǎo)語(yǔ):本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之面向?qū)ο笃闹攸c(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來(lái)一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、明確變量保存的是引用這一本質(zhì);2、熟悉對(duì)象引用的基礎(chǔ)知識(shí);...
摘要:每個(gè)變量都有標(biāo)識(shí)類型和值對(duì)象一旦創(chuàng)建它的標(biāo)識(shí)絕不會(huì)變標(biāo)識(shí)可以簡(jiǎn)單的理解為對(duì)象在內(nèi)存中的地址別名跟是別名指向如果增加新的內(nèi)容也會(huì)增加相等性為運(yùn)算符比較連個(gè)對(duì)象的值對(duì)象中保存的數(shù)據(jù)標(biāo)識(shí)為因?yàn)樗麄兌贾赶蜻@個(gè)列表比較對(duì)象的標(biāo)識(shí)元組相對(duì)不可變性元組保 a = [1,2,3,4] b = a 每個(gè)變量都有標(biāo)識(shí),類型和值.對(duì)象一旦創(chuàng)建,它的標(biāo)識(shí)絕不會(huì)變;標(biāo)識(shí)可以簡(jiǎn)單的理解為對(duì)象在內(nèi)存中的地址. ...
摘要:里,有兩種方法獲得一定范圍內(nèi)的數(shù)字返回一個(gè)列表,還有返回一個(gè)迭代器。在引用計(jì)數(shù)的基礎(chǔ)上,還可以通過(guò)標(biāo)記清除解決容器對(duì)象可能產(chǎn)生的循環(huán)引用的問(wèn)題。列舉常見(jiàn)的內(nèi)置函數(shù)的作用,過(guò)濾函數(shù),循環(huán)函數(shù)累積函數(shù)一行代碼實(shí)現(xiàn)乘法表。 showImg(https://segmentfault.com/img/remote/1460000019294205); 1、為什么學(xué)習(xí)Python? 人生苦短?人間...
摘要:運(yùn)算符比較兩個(gè)對(duì)象的標(biāo)識(shí)函數(shù)返回對(duì)象標(biāo)識(shí)的整數(shù)表示。實(shí)際上,每個(gè)對(duì)象都會(huì)統(tǒng)計(jì)有多少引用指向自己。對(duì)象被銷毀了,調(diào)用了回調(diào),的值變成了。當(dāng)對(duì)象的引用數(shù)量歸零后,垃圾回收程序會(huì)把對(duì)象銷毀。引用的目標(biāo)對(duì)象稱為所指對(duì)象。 對(duì)象不是個(gè)盒子 showImg(https://segmentfault.com/img/bV95mW?w=1784&h=988); class Gizmo: def...
摘要:對(duì)象引用和可變性變量不是盒子,而是便利貼變量的賦值方式比如是將一個(gè)變量分配給一個(gè)對(duì)象比如整數(shù)。運(yùn)算符比較兩個(gè)對(duì)象的標(biāo)識(shí)函數(shù)返回對(duì)象標(biāo)識(shí)的整數(shù)表示。每個(gè)對(duì)象都會(huì)統(tǒng)計(jì)有多少引用指向自己。對(duì)象被銷毀執(zhí)行回調(diào)函數(shù)輸出 對(duì)象引用和可變性 變量不是盒子,而是‘便利貼’ >>> a = [1,2,3] >>> b = a >>> a.append(5) >>> a [1, 2, 3, 5] >>> ...
閱讀 545·2019-08-30 15:55
閱讀 956·2019-08-29 15:35
閱讀 1210·2019-08-29 13:48
閱讀 1923·2019-08-26 13:29
閱讀 2948·2019-08-23 18:26
閱讀 1261·2019-08-23 18:20
閱讀 2843·2019-08-23 16:43
閱讀 2717·2019-08-23 15:58