摘要:運算符比較兩個對象的標識函數返回對象標識的整數表示。實際上,每個對象都會統計有多少引用指向自己。對象被銷毀了,調用了回調,的值變成了。當對象的引用數量歸零后,垃圾回收程序會把對象銷毀。引用的目標對象稱為所指對象。
對象不是個盒子
class Gizmo: def __init__(self): print("Gizmo id: %d" % id(self)) x = Gizmo() print(x) y = Gizmo() * 10 print(y) print(dir())
? 輸出的 Gizmo id: ... 是創建 Gizmo 實例的副作用。
? 在乘法運算中使用 Gizmo 實例會拋出異常。
? 這里表明,在嘗試求積之前其實會創建一個新的 Gizmo 實例。
? 但是,肯定不會創建變量 y,因為在對賦值語句的右邊進行求值時拋出了異常。
longe = {"name": "longe", "born": 1993} liang = longe print(liang is longe) print(id(liang), id(longe)) longe["balance"] = 950 print(liang) ## 冒充的longe信息 other = {"name": "longe", "born": 1993, "balance": 950} print(other) print(other is longe)
? liang 是 longe 的別名。
? is 運算符和 id 函數確認了這一點。
? 向 liang 中添加一個元素相當于向 longe 中添加一個元素。
在那段代碼中,liang 和 longe 是別名,即兩個變量綁定同一個對象。
而 other 不是 longe 的別名,因為二者綁定的是不同的對象。other 和longe 綁定的對象具有相同的值(== 比較的就是值),但是它們的標識不同。
每個變量都有標識、類型和值。對象一旦創建,它的標識絕不會變;
你可以把標識理解為對象在內存中的地址。
is 運算符比較兩個對象的標識;
id() 函數返回對象標識的整數表示。
在==和is之間選擇== 運算符比較兩個對象的值(對象中保存的數據),而 is 比較對象的標識。
is 運算符比 == 速度快,因為它不能重載,所以 Python 不用尋找并調用特殊方法,而是 直接比較兩個整數 ID
eq 方法,會考慮對象屬性的值。相等性測試可能涉及大量處理工作,例如,比較大型集合或嵌套層級深的結構時。
元組的相對不可變性元組的不可變性其實是指 tuple 數據結構的物理內容(即保存的引用)不可變,與引用的對象無關
元組的值會隨著引用的可變對象的變化而變。
元組中不可變的是元素的標識。內存地址
>>> t1 = (1, 2, [30, 40]) ? >>> t2 = (1, 2, [30, 40]) ? >>> t1 == t2 ? True >>> id(t1[-1]) ? 4302515784 >>> t1[-1].append(99) ? >>> t1 (1, 2, [30, 40, 99]) >>> id(t1[-1]) ? 4302515784 >>> t1 == t2 ? False
基礎理解!!!還是可以的默認淺復制
>>> l1 = [3, [55, 44], (7, 8, 9)] >>> l2 = list(l1) ? >>> l2 [3, [55, 44], (7, 8, 9)] >>> l2 == l1 ? True >>> l2 is l1 ? False
然而,構造方法或 [:] 做的是淺復制(即復制了最外層容器,副本中的元素是源容器中
元素的引用)。如果所有元素都是不可變的,那么這樣沒有問題,還能節省內存。
import copy class Bus: def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: self.passengers = list(passengers) def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name) bus1 = Bus(["Alice", "Bill", "Claire", "David"]) bus2 = copy.copy(bus1) bus3 = copy.deepcopy(bus1) print(id(bus1), id(bus2), id(bus3)) bus1.drop("Bill") print(bus2.passengers) print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)) print(bus3.passengers)
? 審查 passengers 屬性后發現,bus1 和 bus2 共享同一個列表對象,因為 bus2 是
bus1 的淺復制副本。
? bus3 是 bus1 的深復制副本,因此它的 passengers 屬性指代另一個列表。
注意,一般來說,深復制不是件簡單的事。如果對象有循環引用,那么這個樸素的算法會進入無限循環深復制
>>> a = [10, 20] >>> b = [a, 30] >>> a.append(b) >>> a [10, 20, [[...], 30]] >>> from copy import deepcopy >>> c = deepcopy(a) >>> c [10, 20, [[...], 30]]
深復制有時可能太深了。例如,對象可能會引用不該復制的外部資源或單例值。我們可以實現特殊方法 __copy__() 和 __deepcopy__(),控制 copy 和 deepcopy 的行為函數的參數作為引用時
共享傳參指函數的各個形式參數獲得實參中各個引用的副本。也就是說,函數內部的形參
是實參的別名。
def f(a, b): a += b return a a = [1, 2] b = [3, 4] print(f(a, b)) print(a, b)
這里變量全都是引用,無論局部變量還是全局.
所以上面案例中,a會變化
class HauntedBus: """備受幽靈乘客折磨的校車""" def __init__(self, passengers=[]): #別使用這種可變類型 作為默認參數 self.passengers = passengers防御性編程(對待可變類型)
class TwilightBus: """正常的校車""" def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: self.passengers = list(passengers) ##這里會產生副本(可以理解為深拷貝) def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name) bus1 = TwilightBus(("sfs", "sdf")) bus2 = TwilightBus(["sdfsdfsfd111"]) bus1.pick("ppxia") bus1.drop("sfs") print(bus1.passengers) bus2.drop("sdfsdfsfd111") print(bus2.passengers)
盡量別用可變類型做默認參數值, 實在要用,必須使其產生副本del和垃圾回收
有個 del 特殊方法,但是它不會銷毀實例,不應該在代碼中調用。
即將銷毀實例時,Python 解釋器會調用 del 方法,給實例最后的機會,釋放外資源。
自己編寫的代碼很少需要實現 del 代碼,有些 Python 新手會花時間實現,但卻吃力不討好,因為 del 很難用對。
垃圾計數器在 CPython 中,垃圾回收使用的主要算法是引用計數。
實際上,每個對象都會統計有多少引用指向自己。
當引用計數歸零時,對象立即就被銷毀:CPython 會在對象上調用__del__ 方法(如果定義了),然后釋放分配給對象的內存。
為了演示對象生命結束時的情形,示例 8-16 使用 weakref.finalize 注冊一個回調函數,在銷毀對象時調用。>>> import weakref >>> s1 = {1, 2, 3} >>> s2 = s1 ? >>> def bye(): ? ... print("Gone with the wind...") ... >>> ender = weakref.finalize(s1, bye) ? >>> ender.alive ? True >>> del s1 >>> ender.alive ? True >>> s2 = "spam" ? Gone with the wind... >>> ender.alive False
? 如前所述,del 不刪除對象,而是刪除對象的引用。
? 重新綁定最后一個引用 s2,讓 {1, 2, 3} 無法獲取。對象被銷毀了,調用了 bye 回
調,ender.alive 的值變成了 False。
正是因為有引用,對象才會在內存中存在。當對象的引用數量歸零后,垃圾回收程序會把對象銷毀。但是,有時需要引用對象,而不讓對象存在的時間超過所需時間。
弱引用不會增加對象的引用數量。引用的目標對象稱為所指對象(referent)。因此我們說,弱引用不會妨礙所指對象被當作垃圾回收。
弱引用在緩存應用中很有用,因為我們不想僅因為被緩存引用著而始終保存緩存對象。
弱引用是可調用的對象,返回的是被引用的對象;>>> import weakref >>> a_set = {0, 1} >>> wref = weakref.ref(a_set) ? >>> wref>>> wref() ? {0, 1} >>> a_set = {2, 3, 4} ? >>> wref() ? {0, 1} >>> wref() is None ? False >>> wref() is None ? True
? 調用 wref() 返回的是被引用的對象,{0, 1}。因為這是控制臺會話,所以 {0, 1}
會綁定給 _ 變量。
? a_set 不再指代 {0, 1} 集合,因此集合的引用數量減少了。但是 _ 變量仍然指代
它。
? 調用 wref() 依舊返回 {0, 1}。
? 計算這個表達式時,{0, 1} 存在,因此 wref() 不是 None。但是,隨后 _ 綁定到結
果值 False?,F在 {0, 1} 沒有強引用了。
? 因為 {0, 1} 對象不存在了,所以 wref() 返回 None。
變量的不是盒子,是便利貼(就是c的指針)
==是值相等 is是(內存地址相等)
默認是淺復制,就內存地址復制.深復制會有一些過深危險(可以重寫特殊方法 __copy__() 和 __deepcopy__())
盡量別用可變類型做默認參數值, 實在要用,必須使其產生副本
實際上,每個對象都會統計有多少引用指向自己。 Cpython中, 當引用計數歸零時,對象立即就被銷毀:CPython會在對象上調用__del__ 方法(如果定義了),然后釋放分配給對象的內存
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/41681.html
摘要:前言本文內容基本摘抄自深入理解虛擬機,以供復習之用,沒有多少參考價值。此區域是唯一一個在虛擬機規范中沒有規定任何情況的區域。堆是所有線程共享的內存區域,在虛擬機啟動時創建。虛擬機上把方法區稱為永久代。 前言 本文內容基本摘抄自《深入理解Java虛擬機》,以供復習之用,沒有多少參考價值。想要更詳細了解請參考原書。 第二章 1.運行時數據區域 showImg(https://segment...
摘要:通常,這種模式是通過定義一個代表處理對象的抽象類來實現的,在抽象類中會定義一個字段來記錄后續對象。工廠模式使用表達式第章中,我們已經知道可以像引用方法一樣引用構造函數。 一、為改善可讀性和靈活性重構代碼 1.改善代碼的可讀性 Java 8的新特性也可以幫助提升代碼的可讀性: 使用Java 8,你可以減少冗長的代碼,讓代碼更易于理解 通過方法引用和Stream API,你的代碼會變得更...
摘要:當引用計數為零,則不再需要該對象且可以銷毀。這表明當變量被刪除時引用計數正確的變為零。方法只能在循環被打破后且引用計數已經為零時調用。這兩步的過程允許引用計數或垃圾收集刪除已引用的對象,讓弱引用懸空。這允許在方法設置對象屬性值之前進行處理。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __del__()方法 ...
摘要:函數的參數作為引用時唯一支持的參數傳遞模式是共享傳參,它指函數的形參獲得實參中各個引用的副本,即形參是實參的別名。而在上面這個例子中,類的屬性實際上是形參所指向的對象所指對象,的別名。 《流暢的Python》筆記本篇是面向對象慣用方法的第一篇,一共六篇。本篇主要是一些概念性的討論,內容有:Python中的變量,對象標識,值,別名,元組的某些特性,深淺復制,引用,函數參數,垃圾回收,de...
閱讀 1014·2023-04-26 02:21
閱讀 2830·2021-09-24 09:47
閱讀 1622·2019-08-30 15:55
閱讀 2176·2019-08-30 14:01
閱讀 2332·2019-08-29 14:01
閱讀 2057·2019-08-29 12:46
閱讀 826·2019-08-26 13:27
閱讀 1951·2019-08-26 12:23