Pythonの内包表記について調べた話
動機
近頃、業務でAWS(特にLambda)を使っている。
Lambdaで「EC2インスタンスのAMIを定期取得」するためのスクリプトをPythonで組もうと思い、色々と調べていた時の事。
上記記事で見慣れない表記を発見。
return sum([ [instance for instance in reservation['Instances']] for reservation in reservations ], [])
sum()
関数は、イテレータブルなオブジェクトの総和を求める関数。
見慣れないのはこの部分。
[instance for instance in reservation['Instances']] for reservation in reservations
リスト内包表記とは
公式ドキュメントは以下。
5. データ構造 — Python 3.6.5 ドキュメント
リスト内包表記はリストを生成する簡潔な手段を提供しています。
主な利用場面は、あるシーケンスや iterable (イテレート可能オブジェクト) のそれぞれの要素に対してある操作を行った結果を要素にしたリストを作ったり、ある条件を満たす要素だけからなる部分シーケンスを作成することです。
基本的な記法
通常のリスト生成はこんな感じ。
list = [] for i in range(10): list.append(i) list # >>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
一方、リスト内包表記を使うとこうなる。
list = [i for i in range(10)] list # >>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
リスト内包表記の基本的な記法は以下。
[i for i in iterator]
if
節の使用
リスト内包表記は、括弧の中の 式、 for 句、そして0個以上の for か if 句で構成されます。 とある。
通常記法:
list = [] for i in range(10): if i % 2 == 0: list.append(i) list # >>> [0, 2, 4, 6, 8]
リスト内包表記:
list = [i for i in range(10) if i % 2 == 0] list # >>> [0, 2, 4, 6, 8]
複数のfor
節の使用
2つのリストから要素を操作することが可能。
以下のコードでは、2つのリストからそれぞれ値の違うもの同士をペアにしている。
[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y] # >>> [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
パフォーマンス
iPython
の%%timeit
で計測してみる。
通常記法:
list = [] for i in range(10000): list.append(i) # >>> 1000 loops, best of 3: 1.08 ms per loop
リスト内包表記:
list = [] list = [i for i in range(10000)] # >>> 1000 loops, best of 3: 337 µs per loop
うむ。速い。
これらを踏まえて
もう一度こちらを見てみる。
[instance for instance in reservation['Instances']] for reservation in reservations
この記法は通常記法にすると
for reservation in reservations: for instance in reservation['Instances']: # instanceに対する処理
と読める。理解が深まった。
いろいろな内包表記
Pythonの内包表記には、リスト内包表記の他にも
- 辞書型内包表記
- ジェネレータ型内包表記
- 集合型内包表記
等があるらしい。奥が深い。
(友人の記事。感謝。)
ジェネレータや集合(set)については、Pythonに於けるイテレータを改めて学んだ後にまとめたいと思う。
(まだまだPython初心者のため、使ったことがない)
今回は使ったことのある辞書についてのみ記述する。
辞書型内包表記
任意のキー:値のペアを作ることが可能。
{key: val for key, val in zip(range(3), ['hoge','fuga','piyo'])} # >>> {0: 'hoge', 1: 'fuga', 2: 'piyo'}
zip()
関数と相性がいいみたい。どこかで使い所はありそう。
まとめ
↓いいところ
- ワンライナーで記述出来る
- 通常記法よりもパフォーマンスがいい
if
やif else
と併用出来、対応力が高い。
↓工夫次第なところ
- 可読性の担保
map()
やfilter()
との使い分け
PythonやRubyは使ってて刺激が多い言語だと改めて感じた。
これから色々試していきたい。
以上。