摘要:對象引用和可變性變量不是盒子,而是便利貼變量的賦值方式比如是將一個(gè)變量分配給一個(gè)對象比如整數(shù)。運(yùn)算符比較兩個(gè)對象的標(biāo)識(shí)函數(shù)返回對象標(biāo)識(shí)的整數(shù)表示。每個(gè)對象都會(huì)統(tǒng)計(jì)有多少引用指向自己。對象被銷毀執(zhí)行回調(diào)函數(shù)輸出
對象引用和可變性
變量不是盒子,而是‘便利貼’
>>> a = [1,2,3] >>> b = a >>> a.append(5) >>> a [1, 2, 3, 5] >>> b [1, 2, 3, 5]
變量的賦值方式:比如x = 2是將一個(gè)變量s分配給一個(gè)對象比如整數(shù)2。而不是把整數(shù)對象2分配給變量s
>>> c = {"name":"yang","born":1997} >>> a = c #a為c的一個(gè)別名。他們倆同時(shí)指向一個(gè)對象,"=="和"is"運(yùn)算符證明這一點(diǎn) >>> a == c True >>> a is c True >>> id(a),id(c) (139644203394464, 139644203394464) >>> a["name"] = "yyy" #用a修改內(nèi)容 >>> c #c也會(huì)被修改,因?yàn)樗鼈儌z指向的是一個(gè)對象 {"name": "yyy", "born": 1997} >>> d = {"name": "yyy", "born": 1997} #新建一個(gè)d對象,與a和c的值一樣 >>> d == a # "=="運(yùn)算符比較值是否相等 True >>> d is a #"is"運(yùn)算符比較對象的標(biāo)識(shí)是否相等,就是比較id()是否相等。d是新建的對象很明顯不會(huì)相等 False >>> id(d), id(a) (139644203394536, 139644203394464)
每個(gè)變量都有標(biāo)識(shí)、類型和值。對象一旦創(chuàng)建,他的標(biāo)識(shí)絕不會(huì)變;你可以把標(biāo)識(shí)理解為對象在內(nèi)存中的地址。is運(yùn)算符比較兩個(gè)對象的標(biāo)識(shí);id()函數(shù)返回對象標(biāo)識(shí)的整數(shù)表示。
元組的不可變性
#元組的不可變性其實(shí)是指tuple數(shù)據(jù)結(jié)構(gòu)的物理內(nèi)容(即保存的引用)不可變,與引用的對象無關(guān)。比如元組里引用了一個(gè)可變對象列表,不能改變這個(gè)引用讓他變成其他字典或整數(shù)對象,但是可以修改這個(gè)可變對象的值。 >>> t1 = (1, 2, [3, 4]) >>> t2 = (1, 2, [3, 4]) >>> id(t1),id(t2) (139644201933272, 139644201953896) >>> t1 == t2 #值相等 True >>> t1 is t2 #標(biāo)識(shí)不相等,兩個(gè)除了值相等其他完全不相關(guān)的變量 False >>> t1[-1].append(5) #可以對元組內(nèi)的列表元組進(jìn)行添加操作 >>> t1 == t2 #此時(shí)他們倆的值不相等了 False
不顯式的使用copy模塊的deepcopy函數(shù)深復(fù)制時(shí),都默認(rèn)為淺復(fù)制
淺復(fù)制復(fù)制了最外層的容器,副本中的元素是原容器中元素的引用
>>> a = [1, 2, [3, 4]] >>> c = a[:] >>>a == c True >>> a is c #容器不一樣,但是里元素的引用一樣 False >>> r = (1, 2, [4,5]) #對元組或其他不可變類型對象淺復(fù)制返回的是同一個(gè)對象的引用。類似于rr = r >>> rr = r[:] >>> rr is r True #淺復(fù)制后母本和副本內(nèi)的元素都互為對方的標(biāo)識(shí),也就是都指向同一個(gè)對象。如果對母本或副本中的可變元素操作,因?yàn)閮蓚€(gè)引用是同一個(gè)對象,所以會(huì)影響到另一個(gè)母本或副本。但是,比如在副本中對不可變元素操作會(huì)生成一個(gè)新的對象引用,就和母本中的不可變元素不是同一個(gè)引用了,就不會(huì)影響到母本。 #下面是示例: >>> l1 = [3, [66,55,44], (7, 8, 9)] >>> l2 = list(l1) #對列表l1淺復(fù)制,賦值給l1 >>> l1.append(100) #l1添加一個(gè)新元素100 >>> l2 [3, [66, 55, 44], (7, 8, 9)] #l2中沒有添加 >>> l1 [3, [66, 55, 44], (7, 8, 9), 100] #l1中添加成功 >>> l1[1].remove(55) #將l1[1]這個(gè)列表中的55元素刪除 >>> l1 [3, [66, 44], (7, 8, 9), 100] >>> l2 [3, [66, 44], (7, 8, 9)] #對l2也有影響,因?yàn)閘2[1]這個(gè)列表和l1[1]的列表時(shí)同一個(gè),他們兩個(gè)互相時(shí)對方的別名,都指向同一個(gè)列表元素 >>> l2[1] += [1,1,1] #l2[1]就地修改列表 >>> l2 [3, [66, 44, 1, 1, 1], (7, 8, 9)] >>> l1 [3, [66, 44, 1, 1, 1], (7, 8, 9), 100] #l1[1]也被修改,因?yàn)檫@是同一個(gè)對象 >>> l1[2] is l2[2] #此時(shí)l1[2]和l2[2]這兩個(gè)元組是同一個(gè)對象 True >>> l2[2] += (1,1,1) #對l2[2]這個(gè)元組添加(1,1,1)。因?yàn)樵M是不可變元素,這個(gè)賦值操作不能就地添加,相當(dāng)于l2[2] = l2[2]+(1,1,1),這里創(chuàng)建了一個(gè)新元組。 >>> l1[2] is l2[2] #此時(shí)l1[2]和l2[2]這兩個(gè)元素不再是同一個(gè)對象 False >>> l2 [3, [66, 44, 1, 1, 1], (7, 8, 9, 1, 1, 1)] >>> l1 [3, [66, 44, 1, 1, 1], (7, 8, 9), 100] #所以這個(gè)修改并沒有對l1起作用 >>> l2[1] is l1[1] #可變對象就地修改,再改還是引用的同一個(gè)對象 True >>> l1[0] is l2[0] True
深復(fù)制
#定義一個(gè)類來測試 class Bus: def __init__(self, p=None): if p is None: p = [] else: self.p = list(p) def pick(self, name): self.p.append(name) def drop(self, name): self.p.remove(name) >>> from bus import * >>> bus1 = Bus(["a", "b", "cc"]) >>> bus1>>> bus1.p ["a", "b", "cc"] >>> bus2 = copy.copy(bus1) #淺復(fù)制bus1 >>> bus3 = copy.deepcopy(bus1) #深復(fù)制bus1 #到此 創(chuàng)建了三個(gè)bus實(shí)例 >>> bus1.drop("a") #bus1的p列表中刪除一個(gè)元素 >>> bus2 >>> bus2.p #bus2的p列表中也沒有這個(gè)元素了,淺復(fù)制共享一個(gè)列表對象 ["b", "cc"] >>> bus3.p #深復(fù)制不會(huì)共享列表,所以不會(huì)修改 ["a", "b", "cc"] >>> bus1.p is bus2.p True >>> bus1.p is bus3.p False
函數(shù)的可變參數(shù)
#函數(shù)可能會(huì)修改接收到的任何可變對象 >>> def f(a, b): ... a += b ... print(id(a)) ... return a >>> x = [1,1] >>> id(x) 139901112369928 >>> y = [2, 2] >>> f(x, y) #函數(shù)的形參獲得各個(gè)實(shí)參的副本,也就是說,函數(shù)內(nèi)部的形參是實(shí)參的別名 139901112369928 [1, 1, 2, 2] >>> x [1, 1, 2, 2] >>> x = 1 #當(dāng)實(shí)參為不可變類型時(shí) >>> y = 2 >>> id(x) 10910400 >>> f(x, y) # a += b相當(dāng)于重新創(chuàng)建了一個(gè)a對象,上面的淺復(fù)制講的很清楚了 10910464 3
函數(shù)的默認(rèn)值是可變參數(shù)時(shí)
class Bus: def __init__(self, p=[]): self.p = p def pick(self, name): self.p.append(name) def drop(self, name): self.p.remove(name) >>> from bus import * >>> bus1 = Bus(["a", "b"]) >>> bus2 = Bus() >>> bus3 = Bus() #創(chuàng)建三個(gè)實(shí)例,bus3和bus3使用默認(rèn)值 >>> bus1.pick("c") #bus1添加新元素 >>> bus1.p,bus2.p,bus3.p #只有bus1變了 (["a", "b", "c"], [], []) >>> bus2.pick("e") #bus2添加新元素 >>> bus1.p,bus2.p,bus3.p #bus2和bus3都變了 (["a", "b", "c"], ["e"], ["e"]) >>> Bus.__init__.__defaults__ #這時(shí)Bus類的defaults屬性已經(jīng)變了 (["e"],)
上面很明顯的說明了:bus2和bus3使用了參數(shù)默認(rèn)值(列表對象)。默認(rèn)值一是在模塊加載時(shí)計(jì)算,self.p變成了p參數(shù)默認(rèn)值的別名。就是說不管多少個(gè)實(shí)例,只要使用的是默認(rèn)值(列表對象),那么所有實(shí)例和Bus類共享這一個(gè)列表。
函數(shù)的默認(rèn)值是可變參數(shù)時(shí)的解決辦法
#錯(cuò)誤的方法 class Bus: def __init__(self, p=None): if p is None: self.p = [] else: self.p = p #self.p為p的別名,他們倆都指向同一個(gè)對象。 def pick(self, name): self.p.append(name) def drop(self, name): self.p.remove(name) >>> from bus import * >>> i = ["a", "b", "c"] >>> b = Bus(i) >>> b.drop("a") # b實(shí)例調(diào)用他的drop方法刪除"a"的時(shí)候把i列表中的"a"也刪了 >>> b.p ["b", "c"] >>> i ["b", "c"] #正確方法 class Bus: def __init__(self, p=None): if p is None: self.p = [] else: self.p = list(p) #這時(shí)self.p是對p的一個(gè)淺復(fù)制,self.p和p指向不同對象,但是容器里面的元素還是相同的引用,如果元素為可變類型,那么還是會(huì)出現(xiàn)問題 def pick(self, name): self.p.append(name) def drop(self, name): self.p.remove(name) >>> from bus import * >>> i = ["a", "b", "c"] >>> b = Bus(i) >>> b.drop("a") # b實(shí)例刪除"a"后i列表并沒有受到影響 >>> i ["a", "b", "c"] >>> i = ["a", "b", [1,2]] #如果參數(shù)內(nèi)元素是可變類型還是有影響 >>> b = Bus(i) >>> b.p[2].pop() # b實(shí)例刪除列表內(nèi)的一個(gè)列表中的元素 2 >>> i # i列表也受到影響 ["a", "b", [1]]
總結(jié):淺復(fù)制復(fù)制的是最外層的容器,里面的元素還是原容器中元素的引用,也就是修改里面的可變元素兩個(gè)容器都會(huì)受到影響。深復(fù)制相當(dāng)于重新創(chuàng)建了一個(gè)對象,里面的元素和原容器一點(diǎn)關(guān)系都沒有。
垃圾回收在cpython中,垃圾回收的主要算法是引用計(jì)數(shù)。每個(gè)對象都會(huì)統(tǒng)計(jì)有多少引用指向自己。當(dāng)計(jì)數(shù)歸零時(shí)對象就立即被銷毀。當(dāng)然python還有其他更復(fù)雜的垃圾回收算法,而且不依賴引用計(jì)數(shù)。
>>> import weakref >>> s1 = {1,2,3} >>> s2 = s1 #s2 is s1。指向同一個(gè)集合 >>> def a(): ... print("aaa") >>> end = weakref.finalize(s1, a) # weakref是一個(gè)弱引用包。這里在s1引用對象上注冊a回調(diào) >>> end>>> del s1 >>> end.alive #對象還沒有被銷毀 True >>> s2 = 2 # 讓s2指向其他對象,此時(shí)沒有對那個(gè)集合的引用。對象被銷毀執(zhí)行回調(diào)函數(shù)輸出"aaa" aaa
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/44131.html
摘要:一對象引用基礎(chǔ)知識(shí)變量是標(biāo)注而不是容器。也就是說元組中不可變的是元素的標(biāo)識(shí),但元組的值會(huì)隨著引用的可變對象變化而變化。在中每個(gè)對象的引用都會(huì)有統(tǒng)計(jì)。弱引用不會(huì)妨礙對象被當(dāng)做垃圾回收。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之面向?qū)ο笃闹攸c(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、明確變量保存的是引用這一本質(zhì);2、熟悉對象引用的基礎(chǔ)知識(shí);...
摘要:每個(gè)變量都有標(biāo)識(shí)類型和值對象一旦創(chuàng)建它的標(biāo)識(shí)絕不會(huì)變標(biāo)識(shí)可以簡單的理解為對象在內(nèi)存中的地址別名跟是別名指向如果增加新的內(nèi)容也會(huì)增加相等性為運(yùn)算符比較連個(gè)對象的值對象中保存的數(shù)據(jù)標(biāo)識(shí)為因?yàn)樗麄兌贾赶蜻@個(gè)列表比較對象的標(biāo)識(shí)元組相對不可變性元組保 a = [1,2,3,4] b = a 每個(gè)變量都有標(biāo)識(shí),類型和值.對象一旦創(chuàng)建,它的標(biāo)識(shí)絕不會(huì)變;標(biāo)識(shí)可以簡單的理解為對象在內(nèi)存中的地址. ...
摘要:函數(shù)的參數(shù)作為引用時(shí)唯一支持的參數(shù)傳遞模式是共享傳參,它指函數(shù)的形參獲得實(shí)參中各個(gè)引用的副本,即形參是實(shí)參的別名。而在上面這個(gè)例子中,類的屬性實(shí)際上是形參所指向的對象所指對象,的別名。 《流暢的Python》筆記本篇是面向?qū)ο髴T用方法的第一篇,一共六篇。本篇主要是一些概念性的討論,內(nèi)容有:Python中的變量,對象標(biāo)識(shí),值,別名,元組的某些特性,深淺復(fù)制,引用,函數(shù)參數(shù),垃圾回收,de...
摘要:運(yùn)算符比較兩個(gè)對象的標(biāo)識(shí)函數(shù)返回對象標(biāo)識(shí)的整數(shù)表示。實(shí)際上,每個(gè)對象都會(huì)統(tǒng)計(jì)有多少引用指向自己。對象被銷毀了,調(diào)用了回調(diào),的值變成了。當(dāng)對象的引用數(shù)量歸零后,垃圾回收程序會(huì)把對象銷毀。引用的目標(biāo)對象稱為所指對象。 對象不是個(gè)盒子 showImg(https://segmentfault.com/img/bV95mW?w=1784&h=988); class Gizmo: def...
摘要:每個(gè)對象均有標(biāo)識(shí)符類型值。通常我們認(rèn)為當(dāng)這些對象被垃圾回收機(jī)制回收時(shí),它占用的外部資源即被釋放。造物主類型對象的類型幾乎影響了該對象的所有功能,在某種程度上,對象的標(biāo)識(shí)符也受其類型的影響。 原文地址 對象 對象(Objects)是python中數(shù)據(jù)的抽象,python中所有的數(shù)據(jù)均可以用對象或者是對象之間的關(guān)系來表示。每個(gè)對象均有標(biāo)識(shí)符(identity)、類型(type)、值(val...
閱讀 719·2021-11-22 13:54
閱讀 3081·2021-09-26 10:16
閱讀 3511·2021-09-08 09:35
閱讀 1590·2019-08-30 15:55
閱讀 3438·2019-08-30 15:54
閱讀 2085·2019-08-30 10:57
閱讀 503·2019-08-29 16:25
閱讀 884·2019-08-29 16:15