函數的參數到底是傳遞的一份復制的值,還是對內存的引用?
我們看下面一段代碼:
a = []
def fun(x):
x.append(1)
fun(a)
print(a)
想想一下:如果傳遞的是一份復制的值,那么列表a應該是不會變化的,還是空列表;如果傳遞的是引用,那么a應該是[1]。
執行一下看到輸出結果是[1],即證明函數參數傳遞的是引用。
但是,再看下面的代碼:
a = 1
def fun(x):
x = 2
return x
ret = fun(a)
print(a)
print(ret)
如果按照上面的理解,函數參數傳遞的是引用,那么a的值應該變為2,但是輸出卻是1,這是為什么?
解釋:
python中所有的變量都可以理解是內存中一個對象的“引用”。這里需要記住的是類型是屬于對象的,而不是變量。而對象有兩種,“可更改”(mutable)與“不可更改”(immutable)對象。在python中,strings, tuples, 和numbers是不可更改的對象,而list,dict等則是可以修改的對象。(這就是這個問題的重點)
當一個引用傳遞給函數的時候,函數自動復制一份引用,這個函數里的引用和外邊的引用沒有半毛關系了。所以第二個例子里函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用是不會改變的。而第一個例子就不一樣了,函數內的引用指向的是可變對象,對它的操作就和定位了指針地址一樣,在內存里進行修改。
不過,有的時候我們確實需要在函數內部修改全局的不可修改的對象,這個時候怎么辦呢?
a = 1
def fun(x):
global a
x = 2
a = 'hello'
return x
ret = fun(a)
print(a)
print(ret)
輸出:
hello
2
我們看到通過關鍵字global,我們將a的值做了修改。但是我們建議盡量減少這種方式的使用,因為我們在函數內部修改了全局變量的值,如果后面還有其他函數使用這個變量的時候可能還認為a的值是1,因此導致出錯,而且不易排查。
另:全局變量約定使用大寫字母。