Pythonの内包表記を覚えよう ~入門編~
こんにちは。今日も寒い!
例えばこんなコード
[ counter for counter in iterator ]
こんな式を表現したいとき。xは数字を羅列したリストです。
こちらはfor文。
def sum_of_squares_b(x): ans = 0 for x_i in x: ans = ans + x_i * x_i return ans
こちらは内包表記。
def sum_of_squares(x): return sum([ (x_i * x_i) for x_i in x ])
両方とも、[ counter_0, counter_1, ...... , counter_n ]のリストが生成された後、全部を合算した合計を返します。
やってることは一緒。
でも、for文で回すより内包表記で回した方がコードも処理時間も短くなります。
[ do_a if a > b else do_b ]
ちょうどkaggleのために書いたコードがあったので、時間計測もくっつけました。
300万行くらいのデータを回した時のやつ。
こちらはfor文+if文。
import time start = time.time() lower = [] for i in range(len(df)): if df["unit_sales1"][i] < df["unit_sales2"][i]: lower.append(df["unit_sales1"][i]) else: lower.append(df["unit_sales2"][i]) print(int(time.time() - start), "秒")
こちらは内包表記。
import time start = time.time() lower = [ [ df["unit_sales1"][i] if df["unit_sales1"][i] < df["unit_sales2"][i] else df["unit_sales2"][i] ] for i in range(len(df)) ] print(int(time.time() - start), "秒")
pandasのDataFrameを使っていて、かつif文つっこんだので内包表記が二重になり。可読性落ちてる……? でもやってることは一緒。
どうして速いの?
検索したらちょうどいい説明があったので引用。
訳注:リストに要素を append() する場合、インタプリタは「リストから append 属性を取り出してそれを関数として呼び出す」という処理をしなければなりません。 それに対して、リスト内包表記を使うと、インタプリタに直接「リストに要素を追加する」という処理をさせることができます。インタプリタが解釈する命令数が減る、属性の取り出しが不要になる、関数呼び出しが不要になる、という3つの理由で、リスト内包表記を使うと速くなります。
- for文で処理する
→「Python側で処理するがためにいちいち受け渡しが発生するから遅い」
- 内包表記で処理する
→「C側でずっと処理するから受け渡しせずに済んで速い」
とざっくり理解することにしました。