1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

複数 List 作成時 for 一回 append より、内包表記で複数 for のほうが速いんだ

Posted at

はじめに

ちわ

久々に Python を触って、ほえ~そっちが早いんだ、となったので久々に記事に

環境

Paiza.io の Python3 で確認

やりたいこと

以下のクラスのリストがあったとして、インスタンス変数ごと(a, b) ごとのリストを作成したい
クラスの数はとりあえず 1 万で

from random import random


class Klass:
    def __init__(self):
        self.a = random()
        self.b = random()
        
klasses = [Klass() for _ in range(10_000)]

# a_list => [klasses[0].a, klasses[1].a, klasses[2].a, ...]
# b_list => [klasses[0].b, klasses[1].b, klasses[2].b, ...]

二つの書き方

ここで私は二つの書き方を思いつきました

append を使用する方法

def list_append(klasses):
    a_list = []
    b_list = []
    for klass in klasses:
        a_list.append(klass.a)
        b_list.append(klass.b)

基本的な書き方

内包表記を使用する方法

def list_comprehensions(klasses):
    a_list = [klass.a for klass in klasses]
    b_list = [klass.b for klass in klasses]

みんな大好き内包表記

内包表記のほうが速いことは有名
でも二回 for 文回してるから append とどっちが速いんだ?

計測 & 結果

それぞれの関数を 100 回実行して平均・標準偏差を見てみる

from time import perf_counter
from statistics import mean, stdev
.
.
.

times = []

for _ in range(100):
    start = perf_counter()
    list_append(klasses)
    times.append(perf_counter() -  start)

print(f"append         {mean(times)}, {stdev(times)}")

times = []

for _ in range(100):
    start = perf_counter()
    list_comprehensions(klasses)
    times.append(perf_counter() -  start)

print(f"comprehensions {mean(times)}, {stdev(times)}")
append         0.0011767360288649797, 2.8312350706089013e-05
comprehensions 0.0007572866510599851, 7.718037436949733e-06

ほえ~ for 文二回まわしてるのに内包表記のほうが、0.0004 秒早い結果に

インスタンス変数多くしてみたら?

じゃあ Klass のインスタンス変数増やして、作成するリスト増やしたら?
何となく 8 つに
これで内包表記は for を 8 回まわしていることになる

from time import perf_counter
from statistics import mean, stdev
from random import random

class Klass:
    def __init__(self):
        self.a = random()
        self.b = random()
        self.c = random()
        self.d = random()
        self.e = random()
        self.f = random()
        self.g = random()
        self.h = random()
        
def list_append(klasses):
    a_list = []
    b_list = []
    c_list = []
    d_list = []
    e_list = []
    f_list = []
    g_list = []
    h_list = []
    for klass in klasses:
        a_list.append(klass.a)
        b_list.append(klass.b)
        c_list.append(klass.c)
        d_list.append(klass.d)
        e_list.append(klass.e)
        f_list.append(klass.f)
        g_list.append(klass.g)
        h_list.append(klass.h)
        
        
def list_comprehensions(klasses):
    a_list = [klass.a for klass in klasses]
    b_list = [klass.b for klass in klasses]
    c_list = [klass.c for klass in klasses]
    d_list = [klass.d for klass in klasses]
    e_list = [klass.e for klass in klasses]
    f_list = [klass.f for klass in klasses]
    g_list = [klass.g for klass in klasses]
    h_list = [klass.h for klass in klasses]

.
. (計測のコード)
.

結果

append         0.004833733141422272, 0.00012474073760079379
comprehensions 0.003598997062072158, 1.9767736938103106e-05

それでも内包表記のほうが速い結果に

まとめ

処理時間を気にして for 文を一つにできないかとか考えがちですが
for 文内で何をしているかが大事ですね。(当たり前ですが)

余談

Paiza.io だとタイムアウトしてしまいましたが、クラスの数を 10 万にすると逆転しました

append         0.04548846199759282, 0.0013987592232240457
comprehensions 0.059213479993632065, 0.0009270777895173936

コード

from time import perf_counter
from statistics import mean, stdev
from random import random

class Klass:
    def __init__(self):
        self.a = random()
        self.b = random()
        self.c = random()
        self.d = random()
        self.e = random()
        self.f = random()
        self.g = random()
        self.h = random()
        
def list_append(klasses):
    a_list = []
    b_list = []
    c_list = []
    d_list = []
    e_list = []
    f_list = []
    g_list = []
    h_list = []
    
    for klass in klasses:
        a_list.append(klass.a)
        b_list.append(klass.b)
        c_list.append(klass.c)
        d_list.append(klass.d)
        e_list.append(klass.e)
        f_list.append(klass.f)
        g_list.append(klass.g)
        h_list.append(klass.h)
        
        
def list_comprehensions(klasses):
    a_list = [klass.a for klass in klasses]
    b_list = [klass.b for klass in klasses]
    c_list = [klass.c for klass in klasses]
    d_list = [klass.d for klass in klasses]
    e_list = [klass.e for klass in klasses]
    f_list = [klass.f for klass in klasses]
    g_list = [klass.g for klass in klasses]
    h_list = [klass.h for klass in klasses]
        
        
klasses = [Klass() for _ in range(10_000)]


times = []

for _ in range(100):
    start = perf_counter()
    list_append(klasses)
    times.append(perf_counter() -  start)

print(f"append         {mean(times)}, {stdev(times)}")


times = []

for _ in range(100):
    start = perf_counter()
    list_comprehensions(klasses)
    times.append(perf_counter() -  start)

print(f"comprehensions {mean(times)}, {stdev(times)}")

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?