可變資料型別不能作為python函式的引數

西西嘛呦發表於2020-10-17

可變資料型別:列表、字典

不可變資料型別:整型、浮點型、字串、元組

為什麼可變資料型別不能作為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

相關文章