for e in collections: pass
在for 循環裏, 最後一個對象e一直存在在上下文中。就是在循環外面,接下來對e的引用仍然有效。html
這裏有個問題容易被忽略,若是在循環以前已經有一個同名對象存在,這個對象是被覆蓋的。java
若是在有代碼感知的IDE中, IDE會提示變量是「被從新聲明的」, 但運行時卻不會出錯。 python
for循環不是閉包,可使用dis模塊分解如下代碼能夠看到:編程
x = 5 for x in range(10): pass print x
將代碼保存到test.py文件,運行python -m dis test.pymarkdown
C:\Users\Patrick\Desktop>python -m dis test.py 1 0 LOAD_CONST 0 (5) 3 STORE_NAME 0 (x) 3 6 SETUP_LOOP 20 (to 29) 9 LOAD_NAME 1 (range) 12 LOAD_CONST 1 (10) 15 CALL_FUNCTION 1 18 GET_ITER >> 19 FOR_ITER 6 (to 28) 22 STORE_NAME 0 (x) 4 25 JUMP_ABSOLUTE 19 >> 28 POP_BLOCK 6 >> 29 LOAD_NAME 0 (x) 32 PRINT_ITEM 33 PRINT_NEWLINE 34 LOAD_CONST 2 (None) 37 RETURN_VALUE
在其餘語言裏,for循環的初始化變量對於上下文一樣是可見的,好比java, 由於java是強類型的語言, 若是從新聲明已存在的變量IDE會提示錯誤, 固然不一樣經過編譯。閉包
一般在python編程中(多是大多數的動態語言),有時即便聲明瞭同名的變量,程序沒有出現明顯的錯誤,可是一旦出錯,錯誤很難被發現。因此要避免與for循環中的變量重名。編輯器
在使用python模板語言編碼時尤爲如此。代碼編輯器沒有提示,不會發現錯誤在哪裏。這個是我碰到的極其怪異的一個例子。爲何說怪異,由於邏輯上沒有任何問題。tornado
在一個頁面模板裏面,當handler調用這個模板時,同時傳遞了兩個對象(從handler中,我使用tornado),一個page對象和一個pages列表。個人順序是這樣的:ui
<!-- 用page對象 --> <label>{{ page.name if page else ''}}</label> <!-- 用pages對象 --> <label>Parent Page <select name="parent_id"> {% if pages %} {% for page in pages%} <option value="{{ page.id}}">{{page.name}}</option> {% end %} {% end %} <option value="">None</option> </select> </label> <!-- 而後又page --> <div>{{ page.markdown if page else ''}}</div>
問題來了,在運行的時候出錯了,提示在 <label>{{ page.name if page else ''}}</label> 中錯誤page referenced before assignment.編碼
暈死了, 找了一夜的錯,最後在把for循環中page的名字改成_page才運行了。
在模板調用過程裏,模板語言也是被翻譯到python字節碼,並按行解析和出,因此根本沒有邏輯,不知道是tornado模板語言的bug。
因此注意變量名。
總之我認爲tornado的exception trace很是不友好。
Python中變量的做用域搜索順序:本地做用域(Local)→當前做用域被嵌入的本地做用域(Enclosing locals)→全局/模塊做用域(Global)→內置做用域(Built-in)