リストの内包表記は、内包表記のスコープが終わっても名前を再バインドします。これは正しいですか? 質問する

リストの内包表記は、内包表記のスコープが終わっても名前を再バインドします。これは正しいですか? 質問する

理解はスコープ指定との異常な相互作用を示します。これは予想される動作ですか?

x = "original value"
squares = [x**2 for x in range(5)]
print(x)  # Prints 4 in Python 2!

文句を言うつもりはありませんが、これはエラーの残酷な原因です。新しいコードを書くと、再バインドが原因で非常に奇妙なエラーが時々見つかります。今でもそれが問題だとわかっています。「リスト内包表記内の一時変数の前には常にアンダースコアを付ける」などのルールを作成する必要がありますが、それでも完璧ではありません。このランダムな時限爆弾が待機しているという事実は、リスト内包表記の優れた「使いやすさ」をすべて無効にしてしまうようなものです。

ベストアンサー1

リスト内包表記はPython 2ではループ制御変数をリークしますが、Python 3ではリークしません。Guido van Rossum (Pythonの作者)はこう述べています。説明この背景にある歴史:

Python 3 では、リストの内包表記とジェネレータ式の同等性を向上させるために、別の変更も行いました。Python 2 では、リストの内包表記によってループ制御変数が周囲のスコープに「漏れ」ます。

x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'

これはリスト内包表記の元の実装の産物であり、長年にわたり Python の「小さな汚い秘密」の 1 つでした。これはリスト内包表記を驚くほど高速にするための意図的な妥協として始まり、初心者にとってはよくある落とし穴ではありませんでしたが、確かに時々人々を困らせました。ジェネレータ式では、これを行うことができませんでした。ジェネレータ式はジェネレータを使用して実装され、その実行には別の実行フレームが必要です。したがって、ジェネレータ式 (特に短いシーケンスを反復する場合) はリスト内包表記よりも効率が悪かったです。

しかし、Python 3 では、ジェネレータ式と同じ実装戦略を使用して、リスト内包表記の「汚い小さな秘密」を修正することにしました。したがって、Python 3 では、上記の例 (print(x) を使用するように変更した後 :-) は 'before' を出力し、リスト内包表記の 'x' が周囲のスコープの 'x' を一時的に隠蔽しますが、上書きしないことを証明します。

おすすめ記事