成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python學(xué)習(xí)之路27-對(duì)象引用、可變性和垃圾回收

Batkid / 1752人閱讀

摘要:函數(shù)的參數(shù)作為引用時(shí)唯一支持的參數(shù)傳遞模式是共享傳參,它指函數(shù)的形參獲得實(shí)參中各個(gè)引用的副本,即形參是實(shí)參的別名。而在上面這個(gè)例子中,類的屬性實(shí)際上是形參所指向的對(duì)象所指對(duì)象,的別名。

《流暢的Python》筆記

本篇是“面向?qū)ο髴T用方法”的第一篇,一共六篇。本篇主要是一些概念性的討論,內(nèi)容有:Python中的變量,對(duì)象標(biāo)識(shí),值,別名,元組的某些特性,深淺復(fù)制,引用,函數(shù)參數(shù),垃圾回收,del命令,弱引用等,比較枯燥,但卻能解決程序中不易察覺(jué)的bug。

1. 變量、標(biāo)識(shí)、相等性和別名

先用一個(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模塊提供的deepcopycopy函數(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ù)制這些值。如果要控制copydeepcopy的行為,我們可以在對(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 = passengersself.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
False
5. 弱引用

不知道大家看到上述代碼第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ù)
>>> wref

>>> 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
5.2 weakref集合

weakref.ref類其實(shí)是底層接口,供高級(jí)用途使用,一般程序最好使用werakref集合和finalize函數(shù),即最好使用WeakKeyDictionary、WeakValueDictionary、WeakSetfinalize(它們?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ì)象類型有限:

基本的listdict實(shí)例不能作為弱引用的所指對(duì)象,但它們的子類則可以;

class MyList(list):
    """MyList的實(shí)例可作為弱引用的所指對(duì)象"""

set的實(shí)例可作為所指對(duì)象;

自定義類的實(shí)例可以;

inttuple的實(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、bytesfrozenset實(shí)例也是如此,并且frozensetcopy方法返回的也不是副本(注意,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

相關(guān)文章

  • Python中的對(duì)象引用變性垃圾回收

    摘要:一對(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í);...

    ytwman 評(píng)論0 收藏0
  • python 對(duì)象引用,變性垃圾回收

    摘要:每個(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)存中的地址. ...

    Flands 評(píng)論0 收藏0
  • Python基礎(chǔ)題目大全,測(cè)試你的水平,鞏固知識(shí)(含答案)

    摘要:里,有兩種方法獲得一定范圍內(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? 人生苦短?人間...

    huhud 評(píng)論0 收藏0
  • 流暢的python讀書筆記-第八章-對(duì)象引用變性垃圾回收

    摘要:運(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...

    zgbgx 評(píng)論0 收藏0
  • python對(duì)象引用,變性垃圾回收

    摘要:對(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] >>> ...

    chavesgu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Batkid

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<