亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Python中的默認參數詳解

系統 1577 0

文章的主題

不要使用可變對象作為函數的默認參數例如 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。

復制代碼 代碼如下:

>>> def function(data=[]):
...???? data.append(1)
...???? return data
...
>>> function()
[1]
>>> function()
[1, 1]
>>> function()
[1, 1, 1]

像你所看到的那樣,list變得越來越長。如果你仔細地查看這個list。你會發現list一直是同一個對象。
復制代碼 代碼如下:

>>> id(function())
12516768
>>> id(function())
12516768
>>> id(function())
12516768

原因很簡單: 在每次函數調用的時候,函數一直再使用同一個list對象。這么使用引起的變化,非常“sticky”。

為什么會發生這種情況?

當且僅當默認參數所在的“def”語句執行的時候,默認參數才會進行計算。請看文檔描述

https://docs.python.org/2/reference/compound_stmts.html#function-definitions
其中有下面一段

"Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call. This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (e.g. by appending an item to a list), the default value is in effect modified. This is generally not what was intended. A way around this is to use None as the default, and explicitly test for it in the body of the function,e.g.:

復制代碼 代碼如下:

def whats_on_the_telly(penguin=None):
??? if penguin is None:
??????? penguin = []
??? penguin.append("property of the zoo")
??? return penguin
"

"def"是Python中的可執行語句,默認參數在"def"的語句環境里被計算。如果你執行了"def"語句多次,每次它都將會創建一個新的函數對象。接下來我們將看到例子。

用什么來代替?

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

復制代碼 代碼如下:

def myfunc(value=None):
??? if value is None:
??????? value = []
??? # modify value here

如果你想要處理任意類型的對象,可以使用sentinel
復制代碼 代碼如下:

sentinel = object()

def myfunc(value=sentinel):
??? if value is sentinel:
??????? value = expression
??? # use/modify value here


在比較老的代碼中,written before “object” was introduced,你有時會看到
復制代碼 代碼如下:

sentinel = ['placeholder']

譯者注:太水,真的不知道怎么翻譯了。我說下我的理解 有時邏輯上可能需要傳遞一個None,而你的默認值可能又不是None,而且還剛好是個列表,列表不
可以寫在默認值位置,所以你需要占位符,但是用None,你又不知道是不是調用者傳遞過來的那個

正確地使用可變參數

最后需要注意的是一些高深的Python代碼經常會利用這個機制的優勢;舉個例子,如果在一個循環里創建一些UI上的按鈕,你可能會嘗試這樣去做:

復制代碼 代碼如下:

for i in range(10):
??? def callback():
??????? print "clicked button", i
??? UI.Button("button %s" % i, callback)

但是你卻發現callback打印出相同的數字(在這個情況下很可能是9)。原因是Python的嵌套作用域只是綁定變量,而不是綁定數值的,所以callback只看到了變量i綁定的最后一個數值。為了避免這種情況,使用顯示綁定。
復制代碼 代碼如下:

for i in range(10):
??? def callback(i=i):
??????? print "clicked button", i
??? UI.Button("button %s" % i, callback)

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

另外的兩個用途local caches/memoization

復制代碼 代碼如下:

def calculate(a, b, c, memo={}):
??? try:
??????? value = memo[a, b, c] # return already calculated value
??? except KeyError:
??????? value = heavy_calculation(a, b, c)
??????? memo[a, b, c] = value # update the memo dictionary
??? return value

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

對高度優化的代碼而言, 會使用局部變量綁全局的變量:

復制代碼 代碼如下:

import math

def this_one_must_be_fast(x, sin=math.sin, cos=math.cos):
??? ...


這是如何工作的?

當Python執行一條def語句時, 它會使用已經準備好的東西(包括函數的代碼對象和函數的上下文屬性),創建了一個新的函數對象。同時,計算了函數的默認參數值。

不同的組件像函數對象的屬性一樣可以使用。上文用到的'function'

復制代碼 代碼如下:

>>> function.func_name
'function'
>>> function.func_code
", line 1>
>>> function.func_defaults
([1, 1, 1],)
>>> function.func_globals
{'function': ,
'__builtins__': ,
'__name__': '__main__', '__doc__': None}

這樣你可以訪問默認參數,你甚至可以修改它。

復制代碼 代碼如下:

>>> function.func_defaults[0][:] = []
>>> function()
[1]
>>> function.func_defaults
([1],)

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

另一個重置默認參數的方法是重新執行相同的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.


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美精品亚洲一区二区在线播放 | 全部免费特黄特色大片中国 | 99热精品久久只有精品黑人 | 国产二区在线播放 | 久久国产视频网 | 五月花精品视频在线观看 | 免费特黄一级欧美大片在线看 | 精品国产线拍大陆久久尤物 | 色网站综合| 99这里有精品视频 | 久久精品视频一区 | 黄黄视频免费看 | 小说区图片区综合久久亚洲 | 免费爱爱网站 | 成人午夜性a一级毛片美女 成人午夜性视频欧美成人 成人午夜亚洲影视在线观看 | 国产亚洲精品第一区在线观看 | 亚洲国产精品热久久2022 | 中文字幕日本一区波多野不卡 | 国产 欧美 在线 | 在线观看精品一区 | 奇米免费视频 | 欧美久久天天综合香蕉伊 | 成年男女免费视频网站 | 精品综合在线 | 精品1区2区3区 | 女人色毛片女人色毛片中国 | 久久精品免看国产成 | 视频一区二区三区在线 | 国产成人小视频在线观看 | 国产黄mmd在线观看免费 | 亚州综合激情另类久久久 | 欧美高清不卡 | 奇米成人| 一级特黄特黄毛片欧美的 | 亚洲精品综合久久中文字幕 | 日本一级高清片免费 | 亚洲欧美另类在线 | 同性女女黄h片在线播放 | 精品一区二区三区在线观看l | 日日夜夜伊人 | 中文字幕一区婷婷久久 |