星期二, 2月 02, 2010

[tips] python2與python 3的list comprehensions之差異

剛好看到這篇
http://yz.mit.edu/wp/2010/01/14/bitten-by-python-scoping/
想起之前也有遇到類似的問題~

其實主要就是python2的list comprehensions有個副作用,
就是會重設scope內的變數, 比如以下這個程式

def t1(): 
x=5
[0 for x in [1,2,3]]
print x

t1()

3

這邊的x會被重設為3, 有點像是殘餘的loop變數的感覺~
有些人會玩弄這個副作用來達到一些不想讓人看懂的lambda技巧就是了:P

但相對來說後來引進的generator語法就沒有這個問題, 所以這個問題其實也可以使用list(x for x in [1,2,3])這個方法來解決.
def ts2():
x=5
list(x for x in [1,2,3])
print x

ts2()

5

另外也可以置換變數比如改成使用a來取代x(當然a是會被assigned).

當然還有另外一個方法則是使用python 3, 就完全沒有這個問題了 :D

5 則留言:

匿名 提到...

越來越偷懶了

James Yang 提到...

Hi, 拜讀過您在 Django 初接教學的影片後, 非常感謝您的speech帶給我的幫助. 最後想問這個article討論的內容是不是因為 list comprehension 是有 local scope 的function, 才會去改變相對於被當作global variable x的value?
當然在tl function外, x 自然是local 變數, 另外自己做了一個測試:

def tl():
x = 5
j = 9
tl.x = 20
[0 for tl.x in [1,2,3]]
print x

tl()
print tl.x
print j

j 會有error, 為何tl.x 不會有呢?
是因為把call tl.x 當作call method底下的local var嗎?
最後這樣tl.x 在外部能算是object嘛?
還是這只是調用method下name是x的變數而已?

python 是相當純物件導向的語言,跟之前學過的java 和c 來講, 需要釐清一些細部觀念, 在此感謝指教。

使徒提姆 !? 提到...

list comprehension並不是function而是個statement,不過在python2裡卻有影響scope內同名變數的副作用, 這個問題在python3已經被解決了, 此外你提的測試中, 因為tl被python視為一個function object,所以x雖然是local variable,tl.x則被視為是其class variable,x跟tl.x是兩個不同scope的變數,而j本身也是個local variable, 所以x,j都無法在function的scope外被存取到,但tl.x可以. 希望這樣有回答到你的問題.

James Yang 提到...

"因為tl被python視為一個function object,所以x雖然是local variable,tl.x則被視為是其class variable,x跟tl.x是兩個不同scope的變數,"

非常critcial 的point, 這樣來看python是所有的一切都是object.
我在做了個測試補上 print tl.a得到了這個結果,
AttributeError: 'function' object has no attribute 'a'
最後試著解釋一下:整個"tl.x"是class variable, x is local var, 但兩者變數是共用同一個值20, That means different var name and type but reference the same value?

James Yang 提到...

x =20;
def tl2():
x=15
print x
tl2()
print tl2.x

>>>AttributeError: 'function' object has no attribute 'x'

用這個範例我清楚明白了.這樣有error,所以兩個變數是完全不同的,一個是class var,another is local var, scope決定了這個var的visible range. 在global scope產生的var是能被local scope存取. 感謝您的解釋 : )