[英]How to Avoid Leaking of Python Assignment Expressions in Comprehensions
在Effective Python一書中,作者建議使用賦值表達式來避免理解中的冗余,例如:
def fun(i):
return 2 * i
result = {x: y for x in [0, 1, 2, 3] if (y := fun(x)) > 3}
代替
result = {x: fun(x) for x in [0, 1, 2, 3] if fun(x) > 3}
result
的值為{2: 4, 3: 6}
。
作者指出
如果推導式在推導式的值部分使用海象運算符並且沒有條件,它會將循環變量泄漏到包含范圍中。 [...] 最好不要泄漏循環變量,因此我建議僅在推導式的條件部分使用賦值表達式。
但是,在上面的示例中, y
在程序結束時設置為 6。 因此,賦值表達式中的變量泄漏了,盡管它是在條件中定義的。
列表推導式也會發生同樣的事情:
>>> _ = [(x, leak) for x in range(4) if (leak := 2 * x) > 3]
>>> leak
6
甚至對於生成器表達式:
>>> it = ((x, leak) for x in range(4) if (leak := 2 * x) > 3)
>>> next(it)
(2, 4)
>>> leak
4
>>> next(it)
(3, 6)
>>> leak
6
我錯過了什么? 有什么方法可以完全避免在推導式中的賦值表達式泄漏?
與 C 或 Java 等其他語言不同,Python 在if
和for
塊中沒有單獨的作用域。 因此,當您在if
語句、 for
循環或列表推導式中使用:=
運算符時,分配的變量將在整個函數或類定義的其余部分都在范圍內。 這也意味着在每個for
循環之后,循環變量仍將在范圍內並包含循環最后一次迭代的值。
如果Effective Python的作者認為這是一件壞事,我不同意他的觀點。 “泄漏”循環變量非常有用! 考慮以下示例:
while line := f.readLine():
if 'Kilian' in line:
break
print('This is the first line that contains your name: ', line)
但是,此規則有一個例外:在列表推導式中進行的隱式賦值有自己的作用域:
>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
這個例外可能是您的困惑的根源。 這只是一種特殊情況,在列表推導式中使用:=
時不適用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.