Python中的預設引數值

acmerfight發表於2013-05-24

伯樂線上注:本文來自文章作者@acmerfight 的投稿(原文)。如果其他朋友也有不錯的原創或譯文,可以嘗試推薦給我們

—————————–

文章的主題

不要使用可變物件作為函式的預設引數例如 list,dict,因為def是一個可執行語句,只有def執行的時候才會計算預設預設引數的值,所以使用預設引數會造成函式執行的時候一直在使用同一個物件,引起bug。

基本原理

在 Python 原始碼中,我們使用def來定義函式或者方法。在其他語言中,類似的東西往往只是一一個語法宣告關鍵字,但def卻是一個可執行的指令。Python程式碼執行的時候先會使用 compile 將其編譯成 PyCodeObject.

PyCodeObject 本質上依然是一種靜態原始碼,只不過以位元組碼方式儲存,因為它面向虛擬機器。因此 Code 關注的是如何執行這些位元組碼,比如棧空間大小,各種常量變數符號列表,以及位元組碼與原始碼行號的對應關係等等。

PyFunctionObject 是執行期產生的。它提供一個動態環境,讓 PyCodeObject 與執行環境關聯起來。同時為函式呼叫提供一系列的上下文屬性,諸如所在模組、全域性名字空間、引數預設值等等。這是def語句執行的時候乾的活。

PyFunctionObject 讓函式面向邏輯,而不僅僅是虛擬機器。PyFunctionObject 和 PyCodeObject 組合起來才是一個完整的函式。

下文翻譯了一篇文章,有一些很好的例子。但是由於水平有限,有些不會翻譯或者有些翻譯有誤,敬請諒解。如果有任何問題請發郵件到 acmerfight圈gmail.com,感激不盡

主要參考資料 書籍:《深入Python程式設計》 大牛:shell 和 Topsky

原文連結

Python對於函式中預設引數的處理往往會給新手造成困擾(但是通常只有一次)。

當你使用“可變”的物件作為函式中作為預設引數時會往往引起問題。因為在這種情況下引數可以在不建立新物件的情況下進行修改,例如 list dict。

像你所看到的那樣,list變得越來越長。如果你仔細地檢視這個list。你會發現list一直是同一個物件。

原因很簡單: 在每次函式呼叫的時候,函式一直再使用同一個list物件。這麼使用引起的變化,非常“sticky”。

為什麼會發生這種情況?

當且僅當預設引數所在的“def”語句執行的時候,預設引數才會進行計算。請看文件描述

http://docs.python.org/ref/function.html

的相關部分。

“def”是Python中的可執行語句,預設引數在”def”的語句環境裡被計算。如果你執行了”def”語句多次,每次它都將會建立一個新的函式物件。接下來我們將看到例子。

用什麼來代替?

像其他人所提到的那樣,用一個佔位符來替代可以修改的預設值。None

 

如果你想要處理任意型別的物件,可以使用sentinel

 

在比較老的程式碼中,written before “object” was introduced,你有時會看到

 

正確地使用可變引數

最後需要注意的是一些高深的Python程式碼經常會利用這個機制的優勢;舉個例子,如果在一個迴圈裡建立一些UI上的按鈕,你可能會嘗試這樣去做:

 

但是你卻發現callback列印出相同的數字(在這個情況下很可能是9)。原因是Python的巢狀作用域只是繫結變數,而不是繫結數值的,所以callback只看到了變數i繫結的最後一個數值。為了避免這種情況,使用顯示繫結。

 

i=i把callback的引數i(一個區域性變數)繫結到了當前外部的i變數的數值上。(譯者注:如果不理解這個例子,請看http://stackoverflow.com/questions/233673/lexical-closures-in-python)

另外的兩個用途local caches/memoization

 

(對一些遞迴演算法非常好用)

對高度優化的程式碼而言, 會使用區域性變數綁全域性的變數:

 

這是如何工作的?

當Python執行一條def語句時, 它會使用已經準備好的東西(包括函式的程式碼物件和函式的上下文屬性),建立了一個新的函式物件。同時,計算了函式的預設引數值。

不同的元件像函式物件的屬性一樣可以使用。上文用到的’function’

 

這樣你可以訪問預設引數,你甚至可以修改它。

然而我不推薦你平時這麼使用。

另一個重置預設引數的方法是重新執行相同的def語句,Python將會和程式碼物件建立一個新的函式物件,並計算預設引數,並且把新建立的函式物件賦值給了和上次相同的變數。但是再次強調,只有你清晰地知道在做什麼的情況下你才能這麼做。

And yes, if you happen to have the pieces but not the function, you can use the function class in the new module to create your own function object.

相關文章