7839

雑草魂エンジニアブログ

【Python】リスト内包表記の使い方まとめ

Python を使って、2次元配列を 1次元配列に変換したり、連想配列の要素である配列要素を抽出して 1つの配列にしたい場合に、for文を使わずに、リスト内包表記でできないんだっけ?と思ったことが、今回の記事を書くきっかけである。そこで、今回はリスト内包表記の理解を深めるべく、自分なりにまとめてみた。

リスト内包表記とは

リスト内包表記では、リスト(配列)やタプルなどのイテラブルオブジェクト(繰り返し可能なオブジェクト)の各要素を、任意の変数名で取り出し、で評価した結果を、新たなリスト(配列)の要素として返却する。基本的には、以下のように書く。

[ 式 for 任意の変数名 in イテラブルオブジェクト ]

リストの要素を取り出して、処理して新しい配列を作りたい場合などによく利用される。(JavaScript をやっている人であれば、map と同じと思ってもらえるといいかもしれない。)for 文との対比することで、書き方のシンプルさを理解してもらえると思う。

# for文の場合
doubles = []
for num in [1, 2, 3, 4]:
    doubles.append( num*2 )

# リスト内包表記の場合
doubles = [ num*2 for num in [1, 2, 3, 4] ]

また、処理速度に関しても、for文と比べてリスト内包表記の方が速いと言われている。

さらに、書き方はほとんど同じであるが、返り値の型を任意の型にしたい場合は、以下のような内包表記が存在する。適宜使い分けていきたいと思えた。

  • 辞書内包表記{ key: value for 変数 in 配列など }
    • 辞書なので、key: value の組み合わせを作る必要がある
  • ジャネレータ内包表記( 式 for 変数 in 配列など )
    • ジェネレータなので、for文などで回さないと要素を取得できない。(ざっくりいうと、式の return をyield に置き換えたようなもの)
    • 順番に要素を取り出すことができる
  • 集合内包表記{ 式 for 変数 in 配列など }
    • set型のオブジェクトである(重複する値を持つことができず、一意な値のみが要素となる。)

if文 + リスト内包表記

if 文と組み合わせることで、リストのフィルターを実現することができる。

[ 式 for 任意の変数名 in イテラブルオブジェクト if 条件式 ]

条件式を満たした任意の変数のみを式で評価して、新しいリストを作ることができる。

members = [
  { "name": "Ken", "age": 18 },
  { "name": "Taro", "age": 20 },
  { "name": "Jiro", "age": 23 },
]
adult = [ member["name"] for member in members if 20 <= member["age"] ]

上記の例では、大人の名前のみを抽出している。

三項演算子 + リスト内包表記

上記の if 文との組み合わせの場合、リストのフィルターとして要素を除外することはできるが、条件に一致しない要素に対して別の処理などをすることができなかった。そこで、別の処理をしたい場合には、三項演算子を使うことで実装できる。

[ 真のときの値 if 条件式 else 偽のときの値 for 任意の変数名 in イテラブルオブジェクト ]

Python三項演算子は、条件式より先に真の結果を書く必要がある。(個人的に、慣れない。。。)

age_class = [ "adult" if 20 <= member["age"] else "child"  for member in members ]

例では、上記と同じ members に対して、年齢から判断して、大人か子供か文字列を返却している。

ネストしたリスト内包表記

冒頭で書いた、2次元配列を 1次元配列に変換したりするには、リスト内包表記をネストさせる必要がある。リスト内包表記の中に for や if が複数回現れる場合、 左側 の for や if が、for 文で書いた場合の外側のブロックになる。 (私は最初、右から読んでいくのかと思い、勘違いしてしまったのでご注意を。)

[ item for inner_list in outer_list for item in inner_list ]

使用例を以下に示す。

matrix = [[0,1,2],[3,4,5],[6,7,8]]
list = [ item for sublist in matrix for item in sublist]
print(list)
# [0,1,2,3,4,5,6,7,8]
 members=[
  {"name": "Ken", "age": 18, "goods": [{"name": "power1", "price": 100}]}, 
  {"name": "Taro", "age": 20, "goods": []}, 
  {"name": "Jiro", "age": 23, "goods": [{"name": "power2", "price": 10}]}
]
items = [item for member in members for item in member["goods"]]
print(items)
# [{'name': 'power1', 'price': 100}, {'name': 'power2', 'price': 10}]

また、複数の変数をまとめて処理することも可能である。

cells = [(row, col) for row in range(3) for col in range(3)]
print(cells)
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

縦 3 x 横 3 の点をプロットする座標を生成することができる。

まとめ

配列操作をどれだけシンプルに書けるかが、コードのシンプルさにも影響してくるので、リスト内包表記はマスターしておきたいところである。また、処理速度も for文より速いとなると、シンプルさと高速化で一石二鳥である。今回、整理してみて、頭の中がクリアになった気がする。

それでは、ステキな開発ライフを。