1.引用
python中變量與對象之間的細節。(或者說 引用和對象分離 )
在python中,如果要使用一個變量,不需要提前進行聲明,只需要在用的時候,給這個變量賦值即可。
例1: a=1
這是一個簡單的賦值語句,其中整數1爲一個對象,a是一個引用,利用賦值語句,引用a指向了對象1。可以通過python的內置函數id()來查看對象的內存地址。
例2:
a=2
print id(a) #24834392
a='banana'
print id(a) #139990659655312
- 1
- 2
- 3
- 4
第一個語句中, 2是儲存在內存中的一個整數對象,通過賦值 引用a 指向了 對象 1;
第二個語句中,內存中建立了一個字符串對象‘banana’,通過賦值 將 引用a 指向了 ‘banana’,同時,對象1不在有引用指向它,它會被python的內存處理機制給當我垃圾回收,釋放內存。
例3:
a=3
print id(a) #10289448
b=3
print id(b) #10289448
- 1
- 2
- 3
- 4
可以看到 這倆個引用 指向了同一個 對象,這是爲什麼呢? 這個跟python的內存機制有關係,因爲對於語言來說,頻繁的進行對象的銷燬和建立,特別浪費性能。所以在Python中,整數和短小的字符,Python都會緩存這些對象,以便重複使用。
(具體詳見python知識點之垃圾回收機制)
例4:
a=4
print id(a) #36151568
b=a #引用b指向引用a指向的那個對象
print id(b) #36151568
a=a+2
print id(a) #36151520
print id(b) #36151568
- 1
- 2
- 3
- 4
- 5
- 6
- 7
第3句對 a 進行了重新賦值,讓它指向了新的 對象6,雖然a 的引用改變了,但是 b 的引用未發生改變,a,b指向不同的對象。
可以得到,即使是多個引用指向同一個對象,如果一個引用值發生變化,那麼實際上是讓這個引用指向一個新的引用,並不影響其他的引用的指向。從效果上看,就是各個引用各自獨立,互不影響。
例5:
引用又分爲指向可變對象(如列表)和指向不可變對象(數字、字符串、元祖)。
L1=[1,2,3]
L2=L1
print id(L1) #1396430512
print id(L2) #1396430512
L1(0)=10
print id(L1) #1396430512
print id(L2) #1396430512
print L2 #[10,2,3]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
與例4相同都修改了其中一個對象的值,但是可以發現 結果 並不相同。L1 和 L2 的引用沒有發生任何變化,但是 列表對象[1,2,3] 的值 變成了 [10,2,3](列表對象改變了)
在該情況下,我們不再對L1這一引用賦值,而是對L1所指向的表的元素賦值。結果是,L2也同時發生變化。
對比例4以及例5可得,引用不可變對象,不能改變對象自身,只是改變引用的指向。引用可變對象,賦值操作可直接改變引用的對象自身(即修改對象的值)。
列表可以通過引用其元素,改變對象自身(in-place change)。這種對象類型,稱爲可變數據對象(mutable object),詞典也是這樣的數據類型。 而像之前的數字和字符串,不能改變對象本身,只能改變引用的指向,稱爲不可變數據對象(immutable object)。
判斷兩個引用所指的對象是否相同,可用is關鍵字:
is是通過對比內存地址(id)來判斷的,返回True 或False。
(擴充:python對象有三要素:id、type、value,
is 用id 判斷 , == 用 value判斷 )
2.拷貝
淺拷貝(copy):拷貝一個對象,但是對象的屬性依然引用原來的,即增加了一個指針指向已經存在的內存。
假設原對象”will”,由於淺拷貝”will”會創建一個新的對象”willber”,所以”will”的 id和”willber”的id不同。(即”wilber is not will”)但是,對於對象中的元素,淺拷貝就只會使用原始元素的引用(內存地址),也就是說”wilber[i] is will[i]”。具體原始對象修改元素如何反映在拷貝後對象上見例6。
深拷貝(deepcopy):增加一個指針並且申請一個新的內存,使這個增加的指針指向新的內存。
例6:
import copy
a=[1,2,3,4,['a','b']] #原始對象
b=a #賦值,傳對象的引用
c=copy.cpoy(a) #淺拷貝
d=cpy.deepcopy(a) #深拷貝
a.append(5) #修改對象a
a[4].append('c') #修改對象中的['a','b']數組對象
print a #[1,2,3,4,['a','b','c'],5]
print b #[1,2,3,4,['a','b','c'],5]
print c #[1,2,3,4,['a','b','c']]
print d #[1,2,3,4,['a','b']]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
首先,a爲可變對象的引用,所以修改a,可變對象的值也隨之改變,a和b指向同一個對象,所以a、b值相同。
其次,c是a 的淺拷貝,淺拷貝的各個元素整體上改變是沒有影響。但是僅元素部分修改是互相牽制的。若修改的元素是不可變類型,比如a[0],則a對應的list的第一個元素會使用一個新的對象,而c依然指向原始對象;若修改的元素是不可變類型,比如a[4],修改操作不會產生新的對象,所以a的修改結果會相應的反應到c上。
最後,d爲深拷貝,有新的內存存儲原始對象,所以不改變。
3.引用問題在函數以及類、實例上的使用:
3.1函數的參數傳遞
a = 1
def fun(a):
a = 2
fun(a)
print a
- 1
- 2
- 3
- 4
- 5
a = []
def fun(a):
a.append(1)
fun(a)
print a # [1]
- 1
- 2
- 3
- 4
- 5
當一個引用傳遞給函數的時候,函數自動複製一份引用,這個函數裏的引用和外邊的引用沒有半毛關係了。
所以第一個例子裏函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用沒半毛感覺。
而第二個例子就不一樣了,函數內的引用指向的是可變對象,對它的操作就和定位了指針地址一樣,在內存裏進行修改。
3.2.實例調用類變量
class Person:
name="aaa"
p1=Person()
p2=Person()
p1.name="bbb"
print p1.name # bbb
print p2.name # aaa
print Person.name # aaa
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
類變量就是供類使用的變量,實例變量就是供實例使用的.
這裏p1.name=”bbb”是實例調用了類變量,這其實和上一個問題一樣,就是函數傳參的問題,p1.name一開始是指向的類變量name=”aaa”,但是在實例的作用域裏把類變量的引用改變了,就變成了一個實例變量,self.name不再引用Person的類變量name了.
class Person:
name=[]
p1=Person()
p2=Person()
p1.name.append(1)
print p1.name # [1]
print p2.name # [1]
print Person.name # [1]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
引用部分參考網址https://www.cnblogs.com/ShaunChen/p/5656971.html
參考http://blog.csdn.net/u013510614/article/details/50751017