1. 程式人生 > >python定義函式時預設引數注意事項

python定義函式時預設引數注意事項

如果在呼叫一個函式時,沒有傳遞預設引數,則函式內的預設引數是對函式的預設引數屬性__defaults__的引用,

      如

      def func(arg1=[]): arg1.append(2)

      呼叫func時如果沒有傳參,上面的arg1就是func.__defaults__[0]的引用

      沒傳遞預設引數,會發生以下情況

      由於func.__defaults__[0]是可變型別,導致每一次呼叫func,arg1都會對func.__defaults__[0]進行操作(func.__defaults__[0].append(2),

      這樣在有些情況下會導致邏輯出錯的,例如

      def func(arg1=[]): if(arg1==[]): print'arg1 is empty' arg1.append(1) else: print'arg1 is not empty'print arg1 func() # arg1 is empty

      func() #arg1 is not empty [1] 

      第二次呼叫func的時候,並沒有傳遞引數arg1,但是第一次呼叫時,函式內部已經修改了__defaults__

      這是為啥呢?為何第二次呼叫不重置arg1為[]?

      因為

      Python的預設引數只會在函式定義時被確定,而不是每次呼叫時重新確定,所以,一旦在函式中修改了預設引數,則再隨後的呼叫中都會生效

      由於有這個特性,在定義函式時,如果預設引數使用可變的物件型別,如上例子,則很可能導致邏輯出錯,

      所以,如不是特別需要,則不允許在函式內部對預設引數引用的func.__defaults__屬性進行修改,如何能讓一個物件不被修改?那就是在操作arg1前取消它對__defaults__的引用

      以上例子改動一下

      def func(arg1=[]): if(arg1==[]): print'arg1 is empty' arg1=[] arg1.append(1) else: print'arg1 is not empty'print arg1

      上例中,在使用者沒有傳遞預設引數arg1時,函式內部會給arg1變數重新賦值,讓arg1去引用我們想用的物件[],這樣arg1就不會修改func.__defaults__了

      如果是預設引數是有值的情況,可以這樣操作

      def func(arg1=[1,2,3]): if(arg1==[1,2,3]): print'is [1,2,3]' arg1=[1,2,3] #重點是這裡,取消arg1對__defaults__屬性的引用,防止arg1修改__defaults__ arg1.append(1) else: print'not [1,2,3]'print arg1

      以上太囉嗦,下例模仿了python官方例子,使用不可變型別(例如None)作為預設引數,邏輯簡潔,推薦使用

      def append_to(element, to=None): #預設引數to定義時為None,但函式邏輯中進行進一步重新賦值為想使用的預設值

      if to is None: to = [1,2,3] to.append(element) return to

      總結:

      防止預設引數修改函式的__defaults__,需要:

      1.定義預設引數時,最好使用不可變型別.

      2.如果預設引數一定要使用可變型別,那就在函式內部對預設引數重新賦值為可變型別的具體值.