リストの内包表記

リストの内包表記はPythonの基本で避けて通れないものの一つのようです。
概念として新しいというよりはあくまで表記の問題なので無理矢理避けて通ろうかと思っていたんですが、コードを読む際にはどうしても必要なのでやはり避け切れませんでした。

リストの内包表記はつまりリストを作成する際、各要素を一つ一つ列挙するのではなく要素を一般化した式で表す方法のことです。
例えば

L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

と同じ物を宣言するのに

L = [i for i in range(10)]

と書きます。読み方が若干ややこしいのですがまずは(上の例で言えば)先頭のiが各要素であり、そのiがどのような値かを後ろのfor節で定義していることになります。iの後ろで一度区切れているということですね。内包表記では先頭の(この例では)iとforが最低1つは存在することになります。

上の例くらいだとまだ分かりやすいんですが次の例だとかなりややこしくなります。

L = [i for i in range(10) if i > 5]

print L #[6, 7, 8, 9]

先頭のiについては最初の例と同じです。その後の空白で一度区切れてfor節も同じなのですが、その後ろのif節はfor節の内側にあることと同じ意味になります。つまりこの内包表記は次のように書くことができます。

L = []

for i in range(10):
    if i > 5:
        L.append(i)

print L #[6, 7, 8, 9]

このように最も左側のfor節を最も外側のループとして、右側の物ほど内側にあると考えることができます。for説を複数含めることもできます。

L = [i*j for i in range(10) for j in range(3)]

右にある物ほど内側なので上の例は

L = []

for i in range(10):
    for j in range(3):
        L.append(i*j)

と同じ意味になり、

[0, 0, 0, 0, 1, 2, 0, 2, 4, 0, 3, 6, 0, 4, 8, 0, 5, 10, 0, 6, 12, 0, 7, 14, 0, 8, 16, 0, 9, 18]

になります。

内包表記はネストすることもできるのですが、これがかなりややこしいです(^_^;)
ネストされた内包表記は多次元リストを返すことになります。

L =[[i*j for i in range(3)]for j in range(5)]

print L #[[0, 0, 0], [0, 1, 2], [0, 2, 4], [0, 3, 6], [0, 4, 8]]

これをループで表すと以下と同義となります。

for j in range(5):
    innerL = []
    for i in range(3):
        innerL.append(i*j)
    
    L.append(innerL)

print L #[[0, 0, 0], [0, 1, 2], [0, 2, 4], [0, 3, 6], [0, 4, 8]]

難しい点はないですがとにかくややこしいので私の頭では2次元リストが精一杯です。

上でも書いたように内包表記を使わなくても同じリストを作成することはできるのですが他の方が書いたコードを読むにはどうしてもこの知識が必要なので十分理解したいところです。