リスト内包表現(list comprehensions)は、既存の反復可能なオブジェクトに基づいてリストを生成するための簡潔な方法で、短い構文を使用します:
squares = [x**2 for x in range(10)]
この記述は、次のように等価です:
squares = [] for x in range(10): squares.append(x**2)
リスト内包表現にはいくつかの利点があります:
evens = [x for x in range(10) if x % 2 == 0];map()のアナロジー:
def f(x): return x**2 squares = list(map(f, range(10)))
mapは、Cで実装された既存の関数を使用する場合、大きなデータでより速く、すべての要素に1つの関数を適用するのに適しています。リスト内包表現は、既存の関数だけでなく任意の式に使用されます。forループは柔軟ですが、冗長です。
なぜ
[x for x in range(10)]のような式では、Python2の実行後に変数xが式の外部で利用可能なのに、Python3では利用できないのか。
回答: Python2では、ループ変数(x)の値はリスト内包表現の実行後も保持されます。Python3では、リストの外では「隔離」され、アクセス不可になり、望ましくない副作用を防ぎます。
例:
# Python 2.x: [x for x in range(3)] print(x) # x == 2 # Python 3.x: [x for x in range(3)] print(x) # NameError: name 'x' is not defined
ストーリー 1
ある開発者は大規模プロジェクトで、リスト内包表現を通じてフィルタリングして新しいリストを作成しようとしました:
my_list = [item.transform() for item in data if item.is_valid()]
しかし、item.is_valid()がFalseを返すと、item.transform()がエラーを引き起こしました。しかし、検証関数は潜在的な副作用を持つように書かれており、最終的にリスト内包表現が副作用のあるコードの一部を不明瞭に破壊しました。
ストーリー 2
プロジェクトがPython2からPython3に移行する際、開発者はループ変数が利用可能であると確信していました:
[x for x in range(5)] print(x) # 4が得られると思ったが、NameErrorが発生した。
これにより、変数がリスト内包表現の外で必要であるという循環ロジックにバグが発生しました。
ストーリー 3
階層を明示しないで入れ子のリスト内包表現を使用する:
def flatten(matrix): return [cell for row in matrix for cell in row]
初心者は、誤った順序での走査(例えば、[cell for cell in row for row in matrix]や余分なネスト)によるエラーをしばしば経験し、誤った結果—一次元リストが得られるか、逆に二次元リストが得られます。