首頁  >  文章  >  後端開發  >  Python 函數參數的預設值為可變物件時需要小心

Python 函數參數的預設值為可變物件時需要小心

WBOY
WBOY轉載
2023-04-22 13:37:071228瀏覽

看到了有給Python 函數參數的預設值傳遞可變對象,以此來加快斐波那契函數的遞歸速度,程式碼如下:

def fib(n, cache={0: 0, 1: 1}):
if n not in cache:
cache[n] = fib(n - 1) + fib(n - 2)
return cache[n]

是不是很新奇,居然可以這樣,速度真的非常快,運行結果如下:

小心此坑:Python 函数参数的默认值是可变对象

不過,我勸你不要這樣做,而且IDE 也會提示你這樣做很不好:

小心此坑:Python 函数参数的默认值是可变对象

這是因為,萬物皆對象,Python 函數也是對象,參數的預設值就是對象的屬性,在編譯階段參數的預設值就已經綁定到該函數,如果是可變對象,Python 函數參數的預設值在會被存儲,並被所有的呼叫者共享,也就是說,一個函數的參數預設值如果是一個可變對象,例如List、Dict,呼叫者A 修改了它,那麼之後呼叫者B 在呼叫的時候看到的就是A 修改後的結果,這樣的模式往往會產生意想不到的結果,例如上面fib 的演算法,但更多的是bug。

可以看下這段簡單的程式碼:

def func(n, li = []):
for i in range(n):
li.append(i)
print(l)

func(2) # [0,1]
func(3,l=[1,2]) # [1,2,0,1,2]
func(2) # [0,1]

你可以先估算這段程式碼的輸出,如果跟註解中的一樣,那你就錯了。正確的結果是:

[0, 1]
[1, 2, 0, 1, 2]
[0, 1, 0, 1]

你可能會覺得,最後一個func(2) 怎麼是這樣,不急,我們print(id(li)) 調試一下:

def func(n, li = []):
print(id(li))
for i in range(n):
li.append(i)
print(li)

func(2)
func(3,li=[1,2])
func(2)

結果如下:

140670243756736
[0, 1]
140670265684928
[1, 2, 0, 1, 2]
140670243756736
[0, 1, 0, 1]

有沒有發現,第一個func(2) 和第二個func(2) 的id 是一樣的,表示它們用到的是li 是同一個,這就參數的預設值是可變物件的邏輯,對於所有的呼叫者來講,是共享的。

如果要深入研究 Python 為什麼這麼設計,可以移步 http://cenalulu.github.io/python/default-mutable-arguments/

#如何避免?

最好的方式是不要使用可變物件作為函數預設值。如果非要這麼用的話,以下是一種解決方案:

def generate_new_list_with(my_list=None, element=None):
if my_list is None:
my_list = []
my_list.append(element)
return my_list

這樣,如果 my_list 預設值永遠都是 []。

最後

我想那個fib 函數的實作可能會讓你印象深刻,不過請注意,這樣的用法非常危險,不可用於自己的程式碼中。

以上是Python 函數參數的預設值為可變物件時需要小心的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:51cto.com。如有侵權,請聯絡admin@php.cn刪除