最近在刷題的時候碰到個奇怪的問題。例子大概是這樣似的:python
In [1]: a = 100 In [2]: def func(): ...: print(a) ...: In [3]: func() 100 In [4]: def another_func(): ...: a += 100 ...: print(a) ...: In [5]: another_func() --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-5-464bd62798b3> in <module>() ----> 1 another_func() <ipython-input-4-1674a1f57571> in another_func() 1 def another_func(): ----> 2 a += 100 3 print(a) 4 UnboundLocalError: local variable 'a' referenced before assignment
爲何直接用print能夠輸出a,可是調用a的話就報錯說變量a未分配?這是Python語言的特性,不一樣於C語言的是**Python函數中的變量(在定義前直接調用時)會優先引用局部變量,查找不到時再去引用全局變量。**可是第二個函數怎麼解釋呢?這裏是由於Python函數中的變量第一次出如今等號右邊時會被當作一個新的局部變量(提早用global聲明過的全局變量除外)。在函數中直接運算a += 100並不會像直接打印a同樣調用全局變量,而調用的是還沒來得及賦值的局部變量a,因此纔會報錯。app
In [1]: def func(num): ...: num += 1 ...: print(id(num)) ...: In [2]: num = 0 In [3]: id(num) Out[3]: 1953497520 In [4]: func(num) 1953497552 In [5]: lists = [1,2,3] In [6]: def list_handle(lists): ...: lists.append(4) ...: In [7]: list_handle(lists) In [8]: lists Out[8]: [1, 2, 3, 4] In [9]: string = 'hello' In [10]: print(id(string)) 1861356044448 In [11]: def string_handle(string): ...: string = ' '.join([string, 'world']) ...: print(id(string)) ...: In [12]: string_handle(string) 1861357160816
如上述代碼所示,若是用形參的方式向函數傳值時原理與C語言相似,是將實參的值複製給函數中的局部變量(本質上是增長新局部變量來引用實參的值)。當實參爲可變對象時可原地修改傳入對象的內容,實參爲不可變對象時修改的只是局部變量的值而對傳入的實參內容沒有影響。函數
In [13]: def list_append(li=[], num=None): ...: li.append(num) ...: print(li) ...: In [14]: list_append(num=1) [1] In [15]: list_append(num=2) [1, 2]
另外還有一點須要注意的是,若是定義函數時設定形參默認值爲可變對象,那麼會出現上面這種狀況。當屢次使用默認值調用函數時並無每次都建立新list並append值進去,而一直操做的是以前的那個list。這是由於Python的函數在初次編譯時,爲可變類型的形參分配一個固定地址,之後在使用缺省值時就一直操做這塊地址,想要避免這種狀況就儘可能不要設置形參的默認值爲可變對象或每次使用時不要用缺省值,也能夠用如下方式:code
In [16]: def list_append(li=None, num=None): ...: if not li: ...: li = [] ...: li.append(num) ...: return li ...: In [17]: list_append(num=1) Out[17]: [1] In [18]: list_append(num=2) Out[18]: [2]