Python變量相似於Java中的引用式變量,所以最好把他們理解爲附加在對象上的標註。python
Python賦值語句應該始終先讀右邊。算法
對象在右邊建立或獲取,再此以後左邊的變量纔會綁定到對象上,這就像爲對象貼上標註。app
變量只不過是標註,因此沒法阻止爲對象貼上多個標註。貼的多個標註,就是別名。 ide
>>> f1 = {'name':'Li','age':19} >>> f2 = f1 # f2是f1的別名 >>> f2 is f1 True
每一個變量(object)都有標識(identity)、類型(type)和值(value)。函數
對象一旦建立,它的標識毫不會變。能夠把標識理解爲對象在內存中的地址。spa
is運算符比較兩個對象的標識,id()函數返回對象標識的整數表示,==運算符比較兩個對象的值(對象中保存的數據)。code
▲ 對+= 或 *= 所作的增量賦值來講,若是左邊的變量綁定的是不可變對象,會建立新對象,若是是可變對象,會就地修改。對象
元組的相對不可變性:blog
元組與多數Python集合(列表、字典、集)同樣,保存的是對象的引用。進程
若是引用的元素是可變的,即使元組自己不可變,元素依然可變。
淺複製:(複製最外層容器,副本中的元素是源容器中元素的引用)
複製列表最簡單的方式:
(1)使用構造方法
(2)使用 [ :]
>>> l1 = [1,2,3,4] >>> l2 = list(l1) >>> l3 = l1[:]
深複製:副本不共享內部對象的引用
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) import copy bus1 = Bus([1,2,3,'David','Tom']) bus2 = copy.copy(bus1) bus3 = copy.deepcopy(bus1) print(id(bus1),id(bus2),id(bus3)) print(id(bus1.passengers),id(bus2.passengers),id(bus3.passengers)) bus1.drop('David') bus1.pick(998) print(bus1.passengers) print(bus2.passengers) print(bus3.passengers)
函數參數做爲引用時:
Python惟一支持的參數傳遞模式:共享傳參(call by sharing)
共享傳參:指函數各個形式參數得到實參中各個引用的副本。(函數內部的形參是實參的副本)
def f(a,b): a += b return a a = 1 b = 2 c = f(a,b) >>> print(c) 3 >>> print(a,b) 1,2 # ======== def f(a,b): a += b return a a = [1,2] b = [3,4] c = f(a,b) >>> print(c) [1, 2, 3, 4] >>> print(a,b) [1, 2, 3, 4] [3, 4]
避免使用可變類型做爲參數的默認值
bus_team = ['Sue','David','Pat','Maria'] bus = Bus(bus_team) bus.drop('David') bus.drop('Pat') >>> bus_team ['Sue','Maria']
在類中直接把參數賦值給實例變量等於爲參數對象建立別名。
class Bus: def __init__(self,bus_team): if bus_team is None: self.bus_passengers = [] else: self.bus_passengers = bus_team # ============= class Bus: def __init__(self,bus_team): if bus_team is None: self.bus_passengers = [] else: self.bus_passengers = list(bus_team)
del和垃圾回收:
del語句刪除名稱,而不是對象。
del命令可能會致使對象被看成垃圾回收,可是僅當刪除的變量保存的是對象的最後一個引用,或者沒法獲得對象時。
在Cpython中,垃圾回收使用的主要算法是引用計數。實際上,每一個對象都會統計有多少引用指向本身。
當引用計數歸零時,對象當即就被銷燬。Cpython會在對象上調用__del__方法,而後釋放分配給對象的內存。
>>> import weakref >>> s1 = {1,2,3} >>> s2 = s1 >>> ender = weakref.finalize(s1,bye) # 註冊一個回調函數 >>> ender.alive True >>> del s1 >>> ender.alive True >>> s2 = '123' Gone with the wind >>> ender.alive False
弱引用:
正由於有引用,對象纔會在內存中存在。
弱引用不會增長對象的引用數量。
引用的目標對象稱爲所指對象(referent),弱引用不會妨礙所指對象被看成垃圾回收。
WeakValueDictionary 類實現的是一種可變映射,裏面的值是對象的弱引用。
被引用的對象在程序中的其餘地方被看成垃圾回收後,對應的鍵會自動從WeakValueDictionary中刪除。
class Cheese: def __init__(self,kind): self.kind = kind >>> import weakref >>> stock = weakref.WeakValueDictionary() >>> catalog = [Cheese('Red Leicester'),Cheese('Tilsit'),Cheese('Parmesan')] >>> for cheese in catalog: stock[chess.kind] = cheese >>> sorted(stock.keys()) ['Parmesan','Red Leicester','Tilsit'] >>> del catalog >>> sorted(stock.keys()) ['Parmesan'] >>> del cheese >>> sorted(stock.keys()) []
▲ for循環中的變量cheese是全局變量,除非顯式刪除,不然不會消失。
weakref 模塊還提供了WeakSet 類,若是一個類須要知道全部實例,一種好的方案是建立一個WeakSet 保存實例引用。
常規Set實例永遠也不會被垃圾回收,由於類中有實例的強引用。而類存在的時間與Python進程同樣長,除非顯式刪除類。
弱引用的侷限:
基本的list和dict實例不能做爲所指對象,可是他們的子類能夠。set實例和用戶自定義類型能夠做爲所指對象。
同時int和tuple實例不能做爲弱引用的目標,甚至它們的子類也不行。
class Mylist(list): ... a_list = Mylist(range(10)) wref_to_a_list = weakref.ref(a_list)