可變資料型別:列表、字典
不可變資料型別:整型、浮點型、字串、元組
為什麼可變資料型別不能作為python函式的引數?請看以下例子:
def foo(a=[]): a.append(1) return a print(foo()) print(foo()) print(foo())
結果:
[1] [1, 1] [1, 1, 1]
我們繼續列印下每次函式的返回值的記憶體地址:
print(id(foo())) print(id(foo())) print(id(foo()))
結果:
140344852133960 140344852133960 140344852133960
會發現我們每次返回的都是同一個物件。
再看下以下例子:
b = [1,2] def test(place=b): place.append(1) return place print(b) print(test()) print(b) print(test()) print(b)
結果:
[1, 2] [1, 2, 1] [1, 2, 1] [1, 2, 1, 1] [1, 2, 1, 1]
當使用列表作為引數傳入函式時,實際上是引用傳遞。也就是傳入的是實際引數的地址,而place=b也就是指向相同的地址。比如以下的:
c = [1,2,3] d = c print(id(c)) print(id(d))
結果:
140344851860104 140344851860104
當我們修改d的值時,同樣也會影響到c:
d.append(4) print(d) print(c) print(id(d)) print(id(c))
結果:
[1, 2, 3, 4] [1, 2, 3, 4] 140344851860104 140344851860104
所以在上述中,通過在test()函式中修改place的值也會影響到b的值。
為什麼會這樣呢?
python中一切皆物件。函式也是物件,可以這麼理解,一個函式是一個被它自己定義而執行的對,;預設引數是一種"成員資料",所以它們的狀態和其他物件一樣,會隨著每一次呼叫而改變。
怎麼避免這種現象呢?
使用以下方式:
def foo(a=None): if a is None: a = [] a.append(1) return a print(foo()) print(foo()) print(foo())
結果:
[1] [1] [1]
如果需要處理任意物件(包括None),則可以使用哨兵物件:
sentinel = object() def myfunc(value=sentinel): if value is sentinel: value = expression # use/modify value here
應用:求陣列的全排列
最後我們來看一個應用例子:求陣列的全排列
基本思路是回溯法:每次從陣列中選一個值加入到tmp中,如果陣列中沒有值了,就將tmp加入到結果中,返回。
如果我們的程式碼是這種:
arr = [1,2,3] res = [] def permutation(arr,tmp=[]): global res if len(arr) == 0: res.append(tmp) return for i in range(len(arr)): tmp = tmp + [arr[i]] newArr = arr[:i]+arr[i+1:] permutation(newArr,tmp) subset(arr,tmp=[]) print(res)
結果:
[[1, 2, 3], [1, 2, 3, 2], [1, 2, 1, 3], [1, 2, 1, 3, 1], [1, 2, 3, 1, 2], [1, 2, 3, 1, 2, 1]]
這裡就出現了上述的情況,我們只需要簡單的改以下即可:
arr = [1,2,3] res = [] def permutation(arr,tmp=[]): global res if len(arr) == 0: res.append(tmp) return for i in range(len(arr)): newTmp = tmp + [arr[i]] newArr = arr[:i]+arr[i+1:] permutation(newArr,newTmp) subset(arr,tmp=[]) print(res)
結果:
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
我們只需要每次新建一個列表即可。
參考:https://blog.csdn.net/rommi/article/details/51979556