# 5. for 文かリスト内包表記か

リスト内包表記の方が速い

「4. for 文か while 文か」の結果でもそうでしたが、一般にリスト内包表記の方が速いそうです。 ここで、ランダムな整数のリストを生成することを考えます。

[random() for i in range(10)]

乱数の生成に random.randoint を使ってしまうと 今回作ったツールで funcscale.py で通らなくなってしまうので、 擬似乱数を生成するクロージャを用意しました。 これは 線形合同法 による擬似乱数生成器です。

昔、競プロの問題を解いていて、線形合同法を知らなくて、 これの規則性を見つける問題と勘違いして、辛い思いをしました笑

def linear_congruential_generators(a, x, b, m):
    def random():
        nonlocal x
        x = (a * x + b) % m
        return x
    return random

random = linear_congruential_generators(48271, 8, 0, 2**31 - 1)

random()  # 386168
random()  # 1460846352
random()  # 1741224500

# 5.1. 比較対象

次の通りです。for 文と for 文(属性参照の無いもの)とリスト内包表記の3つを比較しました。

def for_statement(i):
    random = linear_congruential_generators(48271, 8, 0, 2**31 - 1)
    lst = []
    for _ in range(10**i):
        lst.append(random())
    return lst


def for_statement_speed_up(i):
    random = linear_congruential_generators(48271, 8, 0, 2**31 - 1)
    lst = []
    append = lst.append
    for _ in range(10**i):
        append(random())
    return lst


def for_statement_list_comprehension(i):
    random = linear_congruential_generators(48271, 8, 0, 2**31 - 1)
    return [random() for _ in range(10**i)]

# 5.2. 測定結果

測定結果は以下の通りです。


# Case 0
# (0, )
for_statement                            :   0.0015 [msec]
for_statement_speed_up                   :   0.0015 [msec]
for_statement_list_comprehension         :   0.0015 [msec]

# Case 1
# (1, )
for_statement                            :   0.0060 [msec]
for_statement_speed_up                   :   0.0056 [msec]
for_statement_list_comprehension         :   0.0055 [msec]

# Case 2
# (2, )
for_statement                            :   0.0436 [msec]
for_statement_speed_up                   :   0.0406 [msec]
for_statement_list_comprehension         :   0.0396 [msec]

# Case 3
# (3, )
for_statement                            :   0.4258 [msec]
for_statement_speed_up                   :   0.3927 [msec]
for_statement_list_comprehension         :   0.3898 [msec]

# Case 4
# (4, )
for_statement                            :   4.2091 [msec]
for_statement_speed_up                   :   4.2072 [msec]
for_statement_list_comprehension         :   3.7209 [msec]

# Case 5
# (5, )
for_statement                            :  46.6584 [msec]
for_statement_speed_up                   :  42.4418 [msec]
for_statement_list_comprehension         :  41.5389 [msec]

# Case 6
# (6, )
for_statement                            : 480.8355 [msec]
for_statement_speed_up                   : 457.6269 [msec]
for_statement_list_comprehension         : 425.5688 [msec]

# ◯ なんでリスト内包表記は速いの?

メソッド呼び出しを避けることで、 リスト内包表記に近づいています。 リスト内包表記が速いことの要因の1つです。

実は、元のコードのオーバーヘッドの大半は、 append 属性の参照にあったという事になります。
Pythonの内包表記はなぜ速い? - DSAS開発者の部屋

上記の記事では、バイトコードを比較しながらリスト内包表記と for 文を比較、 解説してくれている、大変ありがたい記事になります。