默認值的做用域python
def foo(xyz=[]): xyz.append(10) print(xyz) foo()#[10] foo()#[10, 10]
爲何第二次調用foo函數打印的是[10, 10]???app
由於函數也是對象,python把函數的默認值放在了屬性中,這個屬性就伴隨着這個函數對象的整個生命週期;ide
查看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__中使用元組保存全部默認值,它不會由於在函數體內使用了它而發生改變。
ci
可變類型默認值,若是使用默認值,就可能修改這個默認值;
作用域
有時候這個特性是好的,有的時候這種特性是很差的,有反作用;it
如何作到按需改變呢?看以下兩種方法:class
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就建立一個列表;
若是傳入一個列表,就修改這個列表;
總結:
第一種方法,使用影子拷貝建立一個新的對象,永遠不能改變傳入的參數
第二種方法,
經過值的判斷就能夠靈活的選擇建立或者修改傳入對象;
這種方式靈活,應用普遍;
不少函數的定義,均可以看到使用None這個不可變的值做爲默認參數,可說這是一種慣法。