先上個示例:html
>>> val = [1] >>> val[0] = val >>> val [[...]]
上述代碼使val中包含自身,而產生了無限遞歸。上述示例代表Python中的變量名爲引用類型,賦值只是使得左值指向與右值相同的內存對象。python
is
運算符能夠判斷兩個引用是否指向了同一個對象,而==
運算符判斷兩個引用指向的值是否相等而不關心指向什麼對象。git
對引用不了解的朋友,能夠把Python引用與C/C++中的void *
類比,不過由於垃圾回收機制Python引用無需擔憂內存泄漏的問題。github
上面的示例代表賦值沒法爲對象創建副本,python中的copy模塊提供了copy
和deepccopy
創建副本。app
示例:函數
>>> import copy >>> a = [[1, 2, 3], [4, 5, 6]] >>> b = a >>> c = copy.copy(a) >>> d = copy.deepcopy(a) >>> a.append(7) >>> a[1][2] = 0 >>> a [[1, 2, 3], [4, 5, 0], 7] >>> b [[1, 2, 3], [4, 5, 0], 7] >>> c [[1, 2, 3], [4, 5, 0]] >>> d [[1, 2, 3], [4, 5, 6]]
淺複製copy.copy只複製父引用指向的對象,其子引用仍指向原來的內存對象,而深複製copy.deepcopy
則會複製全部引用指向的對象。deepcopy 本質上是遞歸 copy。post
示例中的副本c,d父對象是a的副本因此a.append
方法對它們沒有影響。性能
可是copy.copy
建立的副本c中的元素仍指向與a相同的內存對象,而deepcopy
建立的d則指向了本身的元素。ui
tuple和frozenset之類的容器只是保證其中引用指向不變,可是引用指向的內存對象仍然是可變的。容器的切片對象的機制爲淺複製。.net
x = x + y
,必須建立新的臨時變量而後進行淺複製,性能較差。
x += y
,無需新建臨時對象,只在內存塊末尾增長元素,性能較好。
Python中的參數傳遞採用淺複製的值傳遞。
示例:
>>> def swap(a,b): ... b,a=a,b ... >>> a = 1 >>> b = 2 >>> swap(1,2) >>> a 1 >>> b 2
上述示例證實,Python參數傳遞是採用值傳遞的方式。
示例2:
>>> def fun(a): ... a[0] = 2 ... >>> a = [1] >>> fun(a) >>> a [2]
這個示例則證實採用淺複製的方法進行傳遞。
Python中的垃圾回收是以引用計數爲主,標記-清除和分代收集爲輔。
一組對象互相引用的狀況稱爲循環引用(交叉引用),若出現這種狀況引用計數將沒法正確的回收垃圾。,能夠包含其餘對象引用的容器對象(如list, dict, set,甚至class)均可能產生循環引用。
「標記-清除」法是爲了解決循環引用問題。
垃圾標記時,先將集合中對象的引用計數複製一份副本(以避免在操做過程當中破壞真實的引用計數值),而後操做這個副本,遍歷對象集合,將被引用對象的引用計數副本值減1。
根據引用計數副本值是否爲0將集合內的對象分紅兩類,reachable和unreachable,其中unreachable是能夠被回收的對象。
分代回收的總體思想是:將系統中的全部內存塊根據其存活時間劃分爲不一樣的集合,每一個集合就成爲一個「代」,垃圾收集頻率隨着「代」的存活時間的增大而減少,存活時間一般利用通過幾回垃圾回收來度量。
弱引用是避免循環引用的一種方法,弱引用不記錄引用計數。當一個對象只有弱引用時可能被垃圾回收器回收。
weakref.ref(obj,[callable])
用於創建一個指向obj的弱引用,當對象被回收前callable可選參數指定的函數將被執行以進行清理工做。