Pythonのfor文を簡潔に記述するリスト内包表記とは?

Excelをイメージし、以下のような2行4列のデータがあったとします。

2行4列のデータの例

この2行4列のデータは、データ名をrowsとし、pythonで記述すると以下のように表すことができます。Noneはデータが何もないことを表します。

rows = [
    [1, None, 2, None,],
    [3, None, 4, None,],
]

データrowsは、1列目と3列目が整数、2列目と4列目がデータがない状態となっています。

ここで、rows[0][0]は1行1列目の値「1」rows[1][2]は2行3列目の値「4」を表します。このように、rows[i][j]は、データrowsij 列の要素を表します。ただし、pythonでは、i, j はそれぞれ 0 から始まります。つまり、1行目は i=0、3列目は j=2となります。

このとき、以下のような処理を行うと、データrowsの値が文字列に変換されます。ただし、ここでは少し複雑な処理を加え、2列目の値は変換しないようにします。
また、2列目以外は、例えば整数1は文字列 '1' に、Noneは空文字 '' に変換します。

rows = [
    [1, None, 2, None,],
    [3, None, 4, None,],
]
print(rows)


for i, row in enumerate(rows): # データーを文字列化し、Noneは''に変換するループ処理
    for j, col in enumerate(row): # row(各行)のcol(個々の値)を変換
        if j == 1: # j=1(2列目)のデーターは、処理をしない
            continue
        if col == None: # 値がNoneの場合は''(空文字)に変換
            rows[i][j] = ''
        else: # 値がNoneでない場合は文字型に変換
            rows[i][j] = str(col)
print(rows)

上記の、for i, row in enumerate(rows): では、
1回目のループで、i=0, row=[1, None, 2, None,]
2回目のループで、i=1, row=[3, None, 4, None,]となります。
for文のenumerate()使用すると、rowsの要素を順番にrowに代入しながら、 i には 0, 1, と順番に整数が代入されます。

次に、for j, col in enumerate(row): では、
i=0のとき、
1回目のループで、j=0, col=1
2回目のループで、j=1, col=None
3回目のループで、j=2, col=2
4回目のループで、j=3, col=Noneとなり、処理が行われます。
また、i=1のとき、
1回目のループで、j=0, col=3
2回目のループで、j=1, col=None
3回目のループで、j=2, col=4
4回目のループで、j=3, col=Noneとなり、処理が行われます。

if j == 1: continue は、j=1のとき、すなわちデータが2列目のときに、処理が行われないようになります。continue は残りの処理をスキップし、次のループ処理に移りたいときに使用します。

次に、if col == None: rows[i][j] = '' は、値がNoneのとき、空文字 '' に変換します。rows[i][j] は、データrowsij 列の要素(値)を表しています。
ここで、None および '' は、ともにデータがないことを表していますが、Noneは型の情報がないのに対し、'' は型の情報が文字列であることを表します。

続いて、 else: rows[i][j] = str(col) は、値がNoneでない場合、colの値を文字列に変換しています。

以上のような処理を行うと、はじめに [[1, None, 2, None], [3, None, 4, None]] であったデータrowsの各要素の値は、 [['1', None, '2', ''], ['3', None, '4', '']] という形で、文字列に変換されます。
ただし、データの2列目の値は、文字列への変換が行われず、Noneのままの状態となります。

リスト内包表記

リスト内包表記とは、既存のデータ(今回の例ではrows)から新しいリストを作成する手法のことをいいます。リスト内包表記を行うと、複数行で記述されるfor文を1行で記述することができます。

上記の例をリスト内包表記で記述すると、以下のようになります。

rows = [
    [1, None, 2, None,],
    [3, None, 4, None,],
]
print(rows)

rows = [[col if j == 1 else ('' if col is None else str(col)) for j, col in enumerate(row)] for row in rows]
print(rows)

j=1(データが2列目)のときは、col(そのままの値)を採用し何も処理を行わず、j=1以外(データが2列目以外)のときは else に記述されるように、col Noneであれば 空文字列 '' に変換、col Noneでなければ str(col)で文字列に変換しています。

この処理を、各行の row の各値 col についてループします。これにより、for文を複数行で記述した最初の例と同じ結果が導かれます。

まとめ

上記の例から、for文をリスト内包表記するとどのように記述できるのか、イメージだけでもお分かりいただけましたでしょうか?

リスト内包表記は、既存のデータから新たなリストを作成するという特性から、たとえばrowsがリストではなくタプルで定義された場合、リスト内包表記で変換されたrowsはリストとなります。

基本的に、タプルは値を置き換えたり変更を加えることができません。このため、rowsがタプルの場合には、for文が複数行で記述された最初の例を実行すると、 rows[i][j] = '' の箇所でエラーが発生します。

pythonでは、データが「整数」か「文字列」か、「リスト」か「タプル」かなど、型が少しでも違うとエラーが発生する場合があります。また、データがない場合に、「None」なのか 空文字「 '' 」なのかによってもエラーが生じる場合があります。

データを処理する場合は、データの型やタイプにご注意くださいますよう、お願いします。

いいなと思ったら応援しよう!