在Python中,變量是沒有類型的,這和以往看到的大部分編輯語言都不一樣。在使用變量的時候,不需要提前聲明,只需要給這個變量賦值即可。但是,當用變量的時候,必須要給這個變量賦值;如果只寫一個變量,而沒有賦值,那么Python認為這個變量沒有定義。(在python中,對象賦值實際上是對象的引用。當創建一個對象,然后把它賦給另一個變量的時候,python並沒有拷貝這個對象,而只是拷貝了這個對象的引用)
>>> a Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> a NameError: name 'a' is not defined >>>
下面我們具體講一下Python中的變量,引用,拷貝和作用域問題。。
一、可變對象 & 不可變對象
在Python中,對象分為兩種:可變對象和不可變對象,不可變對象包括int,float,long,str,tuple等,可變對象包括list,set,dict等。需要注意的是:這里說的不可變指的是值的不可變。對於不可變類型的變量,如果要更改變量,則會創建一個新值,把變量綁定到新值上,而舊值如果沒有被引用就等待垃圾回收。另外,不可變的類型可以計算hash值,作為字典的key。可變類型數據對對象操作的時候,不需要再在其他地方申請內存,只需要在此對象后面連續申請(+/-)即可,也就是它的內存地址會保持不變,但區域會變長或者變短。
二、變量無類型,對象有類型
上面說了,Python中的變量是沒有類型的,但Python其實是區分類型的:Python的所有變量其實都是指向內存中的對象的一個指針,都是值的引用,而其類型是跟着對象走的。總結來說:在Python中,類型是屬於對象的,而不是變量, 變量和對象是分離的,對象是內存中儲存數據的實體,變量則是指向對象的指針。在《Learning Python》一書中有一個觀點:變量無類型,對象有類型,大概也是說的這個意思。
例如:
對象VS變量
nfoo =
1 #一個指向int數據類型的nfoo(再次提醒,nfoo沒有類型)
lstFoo = [
1] #一個指向list類型的lstFoo,這個list中包含一個整數1
|
下面是一張說明變量的圖:
三、Python函數參數到底是按值還是按引用傳遞的
def func_int(a): a += 4 def func_list(a_list): a_list[0] = 4 t = 0 func_int(t) print t # output: 0 t_list = [1, 2, 3] func_list(t_list) print t_list # output: [4, 2, 3]
對於上面的輸出,不少Python初學者都比較疑惑:第一個例子看起來像是傳值,而第二個例子確實傳引用。其實,解釋這個問題也非常容易,主要是因為可變對象和不可變對象的原因:對於可變對象,對象的操作不會重建對象,而對於不可變對象,每一次操作就重建新的對象。
在函數參數傳遞的時候,Python其實就是把參數里傳入的變量對應的對象的引用依次賦值給對應的函數內部變量。參照上面的例子來說明更容易理解,func_int中的局部變量"a"其實是全部變量"t"所指向對象的另一個引用,由於整數對象是不可變的,(你想修改不可更改的對象時,其實就是開辟了一個新的存儲空間新的對象)所以當func_int對變量"a"進行修改的時候,實際上是將局部變量"a"指向到了整數對象"1"。所以很明顯,func_list修改的是一個可變的對象,局部變量"a"和全局變量"t_list"指向的還是同一個對象。