国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

python對象引用,可變性和垃圾回收

chavesgu / 1089人閱讀

摘要:對象引用和可變性變量不是盒子,而是便利貼變量的賦值方式比如是將一個變量分配給一個對象比如整數。運算符比較兩個對象的標識函數返回對象標識的整數表示。每個對象都會統計有多少引用指向自己。對象被銷毀執行回調函數輸出

對象引用和可變性

變量不是盒子,而是‘便利貼’

>>> a = [1,2,3]
>>> b = a
>>> a.append(5)
>>> a
[1, 2, 3, 5]
>>> b
[1, 2, 3, 5]


變量的賦值方式:比如x = 2是將一個變量s分配給一個對象比如整數2。而不是把整數對象2分配給變量s

>>> c = {"name":"yang","born":1997}
>>> a = c  #a為c的一個別名。他們倆同時指向一個對象,"=="和"is"運算符證明這一點
>>> a == c
True
>>> a is c
True
>>> id(a),id(c)
(139644203394464, 139644203394464)
>>> a["name"] = "yyy"  #用a修改內容
>>> c   #c也會被修改,因為它們倆指向的是一個對象
{"name": "yyy", "born": 1997}
>>> d = {"name": "yyy", "born": 1997} #新建一個d對象,與a和c的值一樣
>>> d == a  # "=="運算符比較值是否相等
True
>>> d is a  #"is"運算符比較對象的標識是否相等,就是比較id()是否相等。d是新建的對象很明顯不會相等
False
>>> id(d), id(a)
(139644203394536, 139644203394464)

每個變量都有標識、類型和值。對象一旦創建,他的標識絕不會變;你可以把標識理解為對象在內存中的地址。is運算符比較兩個對象的標識;id()函數返回對象標識的整數表示。

元組的不可變性

#元組的不可變性其實是指tuple數據結構的物理內容(即保存的引用)不可變,與引用的對象無關。比如元組里引用了一個可變對象列表,不能改變這個引用讓他變成其他字典或整數對象,但是可以修改這個可變對象的值。
>>> t1 = (1, 2, [3, 4])
>>> t2 = (1, 2, [3, 4])
>>> id(t1),id(t2)
(139644201933272, 139644201953896)
>>> t1 == t2  #值相等
True
>>> t1 is t2  #標識不相等,兩個除了值相等其他完全不相關的變量
False
>>> t1[-1].append(5)  #可以對元組內的列表元組進行添加操作
>>> t1 == t2  #此時他們倆的值不相等了
False

不顯式的使用copy模塊的deepcopy函數深復制時,都默認為淺復制

淺復制復制了最外層的容器,副本中的元素是原容器中元素的引用

>>> a = [1, 2, [3, 4]]
>>> c = a[:]
>>>a == c
True
>>> a is c  #容器不一樣,但是里元素的引用一樣
False

>>> r = (1, 2, [4,5])  #對元組或其他不可變類型對象淺復制返回的是同一個對象的引用。類似于rr = r
>>> rr = r[:]
>>> rr is r
True

#淺復制后母本和副本內的元素都互為對方的標識,也就是都指向同一個對象。如果對母本或副本中的可變元素操作,因為兩個引用是同一個對象,所以會影響到另一個母本或副本。但是,比如在副本中對不可變元素操作會生成一個新的對象引用,就和母本中的不可變元素不是同一個引用了,就不會影響到母本。
#下面是示例:
>>> l1 = [3, [66,55,44], (7, 8, 9)]
>>> l2 = list(l1)  #對列表l1淺復制,賦值給l1
>>> l1.append(100)  #l1添加一個新元素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]這個列表中的55元素刪除
>>> l1
[3, [66, 44], (7, 8, 9), 100]
>>> l2
[3, [66, 44], (7, 8, 9)] #對l2也有影響,因為l2[1]這個列表和l1[1]的列表時同一個,他們兩個互相時對方的別名,都指向同一個列表元素
>>> 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]也被修改,因為這是同一個對象
>>> l1[2] is l2[2] #此時l1[2]和l2[2]這兩個元組是同一個對象
True
>>> l2[2] += (1,1,1)  #對l2[2]這個元組添加(1,1,1)。因為元組是不可變元素,這個賦值操作不能就地添加,相當于l2[2] = l2[2]+(1,1,1),這里創建了一個新元組。
>>> l1[2] is l2[2] #此時l1[2]和l2[2]這兩個元素不再是同一個對象
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] #所以這個修改并沒有對l1起作用

>>> l2[1] is l1[1] #可變對象就地修改,再改還是引用的同一個對象
True
>>> l1[0] is l2[0]
True

深復制

#定義一個類來測試
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)   #淺復制bus1
>>> bus3 = copy.deepcopy(bus1)  #深復制bus1
#到此 創建了三個bus實例
>>> bus1.drop("a")  #bus1的p列表中刪除一個元素
>>> bus2

>>> bus2.p  #bus2的p列表中也沒有這個元素了,淺復制共享一個列表對象
["b", "cc"]
>>> bus3.p  #深復制不會共享列表,所以不會修改
["a", "b", "cc"]
>>> bus1.p is bus2.p
True
>>> bus1.p is bus3.p
False

函數的可變參數

#函數可能會修改接收到的任何可變對象
>>> def f(a, b):
...     a += b
...     print(id(a))
...     return a
>>> x = [1,1]
>>> id(x)
139901112369928
>>> y = [2, 2]
>>> f(x, y) #函數的形參獲得各個實參的副本,也就是說,函數內部的形參是實參的別名
139901112369928  
[1, 1, 2, 2]
>>> x
[1, 1, 2, 2]
>>> x = 1  #當實參為不可變類型時
>>> y = 2
>>> id(x)
10910400
>>> f(x, y) # a += b相當于重新創建了一個a對象,上面的淺復制講的很清楚了
10910464
3

函數的默認值是可變參數時

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()  #創建三個實例,bus3和bus3使用默認值
>>> 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__ #這時Bus類的defaults屬性已經變了
(["e"],)

上面很明顯的說明了:bus2和bus3使用了參數默認值(列表對象)。默認值一是在模塊加載時計算,self.p變成了p參數默認值的別名。就是說不管多少個實例,只要使用的是默認值(列表對象),那么所有實例和Bus類共享這一個列表。

函數的默認值是可變參數時的解決辦法

#錯誤的方法
class Bus:
    def __init__(self, p=None):
        if p is None:
            self.p = []
        else:
            self.p = p #self.p為p的別名,他們倆都指向同一個對象。

    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實例調用他的drop方法刪除"a"的時候把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) #這時self.p是對p的一個淺復制,self.p和p指向不同對象,但是容器里面的元素還是相同的引用,如果元素為可變類型,那么還是會出現問題

    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實例刪除"a"后i列表并沒有受到影響
>>> i
["a", "b", "c"]

>>> i = ["a", "b", [1,2]]  #如果參數內元素是可變類型還是有影響
>>> b = Bus(i)
>>> b.p[2].pop()  # b實例刪除列表內的一個列表中的元素
2
>>> i  # i列表也受到影響
["a", "b", [1]]

總結:淺復制復制的是最外層的容器,里面的元素還是原容器中元素的引用,也就是修改里面的可變元素兩個容器都會受到影響。深復制相當于重新創建了一個對象,里面的元素和原容器一點關系都沒有。

垃圾回收

在cpython中,垃圾回收的主要算法是引用計數。每個對象都會統計有多少引用指向自己。當計數歸零時對象就立即被銷毀。當然python還有其他更復雜的垃圾回收算法,而且不依賴引用計數。

>>> import weakref
>>> s1 = {1,2,3}
>>> s2 = s1  #s2 is s1。指向同一個集合
>>> def a():
...     print("aaa")
>>> end = weakref.finalize(s1, a)  # weakref是一個弱引用包。這里在s1引用對象上注冊a回調
>>> end

>>> del s1
>>> end.alive  #對象還沒有被銷毀
True
>>> s2 = 2  # 讓s2指向其他對象,此時沒有對那個集合的引用。對象被銷毀執行回調函數輸出"aaa"
aaa


文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44131.html

相關文章

  • Python中的對象引用、變性垃圾回收

    摘要:一對象引用基礎知識變量是標注而不是容器。也就是說元組中不可變的是元素的標識,但元組的值會隨著引用的可變對象變化而變化。在中每個對象的引用都會有統計。弱引用不會妨礙對象被當做垃圾回收。 導語:本文章記錄了本人在學習Python基礎之面向對象篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、明確變量保存的是引用這一本質;2、熟悉對象引用的基礎知識;...

    ytwman 評論0 收藏0
  • python 對象引用,變性垃圾回收

    摘要:每個變量都有標識類型和值對象一旦創建它的標識絕不會變標識可以簡單的理解為對象在內存中的地址別名跟是別名指向如果增加新的內容也會增加相等性為運算符比較連個對象的值對象中保存的數據標識為因為他們都指向這個列表比較對象的標識元組相對不可變性元組保 a = [1,2,3,4] b = a 每個變量都有標識,類型和值.對象一旦創建,它的標識絕不會變;標識可以簡單的理解為對象在內存中的地址. ...

    Flands 評論0 收藏0
  • Python學習之路27-對象引用變性垃圾回收

    摘要:函數的參數作為引用時唯一支持的參數傳遞模式是共享傳參,它指函數的形參獲得實參中各個引用的副本,即形參是實參的別名。而在上面這個例子中,類的屬性實際上是形參所指向的對象所指對象,的別名。 《流暢的Python》筆記本篇是面向對象慣用方法的第一篇,一共六篇。本篇主要是一些概念性的討論,內容有:Python中的變量,對象標識,值,別名,元組的某些特性,深淺復制,引用,函數參數,垃圾回收,de...

    Batkid 評論0 收藏0
  • 流暢的python讀書筆記-第八章-對象引用變性垃圾回收

    摘要:運算符比較兩個對象的標識函數返回對象標識的整數表示。實際上,每個對象都會統計有多少引用指向自己。對象被銷毀了,調用了回調,的值變成了。當對象的引用數量歸零后,垃圾回收程序會把對象銷毀。引用的目標對象稱為所指對象。 對象不是個盒子 showImg(https://segmentfault.com/img/bV95mW?w=1784&h=988); class Gizmo: def...

    zgbgx 評論0 收藏0
  • 萬物之基礎——對象

    摘要:每個對象均有標識符類型值。通常我們認為當這些對象被垃圾回收機制回收時,它占用的外部資源即被釋放。造物主類型對象的類型幾乎影響了該對象的所有功能,在某種程度上,對象的標識符也受其類型的影響。 原文地址 對象 對象(Objects)是python中數據的抽象,python中所有的數據均可以用對象或者是對象之間的關系來表示。每個對象均有標識符(identity)、類型(type)、值(val...

    douzifly 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<