week04_python函數缺省值

默認值的作用域

def foo(xyz=[]):
    xyz.append(10)
    print(xyz)

foo()#[10]
foo()#[10, 10]

爲什麼第二次調用foo函數打印的是[10, 10]???

  因爲函數也是對象,python把函數的默認值放在了屬性中,這個屬性就伴隨着這個函數對象的整個生命週期;

  查看foo.__defaults__屬性;

def foo(xyz=[], u='abc', z=123):
    xyz.append(1)
    return xyz

print(foo(), id(foo))#[1] 12588984
print(foo.__defaults__)#([1], 'abc', 123)
print(foo(), id(foo))#[1, 1] 12588984
print(foo.__defaults__)#([1, 1], 'abc', 123)

函數地址並沒有變,就是說函數這個對象的沒有變,調用它,它的屬性__defaults__中使用元組保存所有默認值;

xyz默認值是引用類型,引用類型的元素變動,並不是元組的變化;

非引用類型的例子:

def foo(w, u='abc', z=123):
    u = 'xyz'
    z = 789
    print(w, u, z)

print(foo.__defaults__)#('abc', 123)
foo('magedu')#magedu xyz 789
print(foo.__defaults__)#('abc', 123)

屬性__defaults__中使用元組保存所有默認值,它不會因爲在函數體內使用了它而發生改變。



可變類型默認值,如果使用默認值,就可能修改這個默認值;

有時候這個特性是好的,有的時候這種特性是不好的,有副作用;

如何做到按需改變呢?看如下兩種方法:

def foo(xyz=[], u='abc', z=123):
    xyz = xyz[:]#影子拷貝
    xyz.append(1)
    print(xyz)

print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10, 5])
print(foo.__defaults__)

函數體內,不改變默認值:

  xyz都是傳入參數或默認參數的副本,如果就想修改原參數,無能爲力;

def foo(xyz=None, u='abc', z=123):
    if xyz is None:
        xyz = []
    xyz.append(1)
    print(xyz)

foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10, 5])
print(foo.__defaults__)

使用不可變類型默認值:

  如果使用缺省值None就創建一個列表;

  如果傳入一個列表,就修改這個列表;


總結:

  1. 第一種方法,使用影子拷貝創建一個新的對象,永遠不能改變傳入的參數

  2. 第二種方法,

    通過值的判斷就可以靈活的選擇創建或者修改傳入對象;

    這種方式靈活,應用廣泛;

    很多函數的定義,都可以看到使用None這個不可變的值作爲默認參數,可說這是一種慣法。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章