国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

講解Python中for循環下的索引變量的作用域

2020-02-23 00:44:19
字體:
來源:轉載
供稿:網友

我們從一個測試開始。下面這個函數的功能是什么?
 

def foo(lst):  a = 0  for i in lst:    a += i  b = 1  for t in lst:    b *= i  return a, b

如果你覺得它的功能是“計算lst中所有元素的和與積”,不要沮喪。通常很難發現這里的錯誤。如果在大堆真實的代碼中發現了這個錯誤就非常厲害了。——當你不知道這是一個測試時,很難發現這個錯誤。

這里的錯誤是在第二個循環體中使用了i而不是t。等下,這到底是怎么工作的?i在第一個循環外應該是不可見的? [1]哦,不。事實上,Python正式聲明過,為for循環目標(loop target)定義的名稱(更嚴格的正式名稱為“索引變量”)能泄露到外圍函數范圍。因此下面的代碼:
 

for i in [1, 2, 3]:  passprint(i)

這段代碼是有效的,可以打印出3。在本文中,我想探討一下為什么會這樣,為什么它不太可能改變,以及將它作為一顆追蹤子彈來挖掘CPython編輯器中一些有趣的部分。

順便說一句,如果你不相信這種行為可能會導致真正的問題,考慮這個代碼片斷:
 

def foo():  lst = []  for i in range(4):    lst.append(lambda: i)  print([f() for f in lst])

如果你期待上面的代碼能打印出[0,1,2,3],你的期望會落空的,它會打印出[3,3,3,3];因為在foo的作用域內只有一個i,這個i就是所有的lambda所捕獲的。
官方說明

Python參考文檔中的for循環部分明確地記錄了這種行為:

    for循環將變量賦值到目標列表中。……當循環結束時,賦值列表中的變量不會被刪除,但如果序列是空的,它們將不會被賦值給所有的循環。

注意最后一句,讓我們試試:
 

for i in []:  passprint(i)

的確,上面的代碼拋出NameError異常。稍后,我們將看到這是Python虛擬機執行字節碼方式的必然結果。
為什么會是這樣

其實我問過Guido van Rossum有關這個執行行為的原因,他很慷慨地告訴了我其中的一些歷史背景(感謝Guido!)。這樣執行代碼的動機是保持Python獲得變量和作用域的簡單性,而不訴諸于hacks(例如在循環完成后,刪除定義在該循環中的所有變量——想想它可能引發的異常)或更復雜的作用域規則。

Python的作用域規則非常簡單、優雅:模塊、類以及函數的代碼塊可引入作用域。在函數體內,變量從它們定義到代碼塊結束(包括嵌套的代碼塊如嵌套函數)都是可見的。當然,對于局部變量、全局變量(以及其他nonlocal變量)其規則略有不同。不過,這和我們的討論沒有太多關系。

這里最重要的一點是:最內層的可能作用域是一個函數體。不是一個for循環體。不是一個with代碼塊。Python與其他編程語言不同(例如C及其后代語言),在函數水平下沒有嵌套詞法作用域。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 当涂县| 滨海县| 阿克苏市| 迭部县| 宜州市| 翁牛特旗| 德兴市| 华池县| 宜兴市| 离岛区| 天台县| 汨罗市| 广河县| 凉山| 固镇县| 万安县| 大安市| 长沙市| 奉新县| 隆德县| 赤壁市| 崇义县| 图们市| 宁远县| 左云县| 四川省| 迭部县| 菏泽市| 屯昌县| 独山县| 文山县| 青海省| 鄱阳县| 芮城县| 运城市| 布拖县| 溆浦县| 兴宁市| 观塘区| 沙湾县| 施甸县|