短いpythonリストの前に付ける慣用的な構文は何ですか?


543

list.append()リストの最後に追加するのは明らかな選択です。ここにあります合理的な説明不足しているためにlist.prepend()。私のリストが短く、パフォーマンスの問題が無視できると仮定すると、

list.insert(0, x)

または

list[0:0] = [x]

慣用句?

回答:


784

s.insert(0, x)フォームは、最も一般的です。

しかし、それを目にしたときはいつでも、リストの代わりにcollections.dequeを使用することを検討するときかもしれません。


9
「しかし、それを目にしたときはいつでも、リストの代わりにcollections.dequeを使用することを検討するときかもしれません。」どうしてこれなの?
マットM.

6
@MattM。リストの前に挿入すると、Pythonは他のすべてのアイテムを1スペース前に移動する必要があり、リストは「前にスペースを作る」ことができません。collections.deque(両端キュー)は、「前にスペースを作る」ことをサポートしており、この場合ははるかに高速です。
fejfo

265

あなたが機能的な方法で行くことができれば、以下はかなり明確です

new_list = [x] + your_list

もちろん、あなたはに挿入xしていません。むしろ、それに先行your_listする新しいリストをx作成しました。


45
お気づきのように、これはリストの先頭にはありません。新しいリストを作成しています。したがって、それは問題をまったく満たしていません。
Chris Morgan

112
それは質問を満たしていませんが、それを丸めて、それがこのウェブサイトの目的です。コメントに感謝し、あなたは正しいですが、人々がこれを検索するとき、これを見るのは役に立ちます。
dave4jr

2
また、リストをリストの先頭に追加したい場合、挿入を使用しても期待どおりに動作しません。しかし、この方法はそうです!
下駄

90

短いpythonリストの前に付ける慣用的な構文は何ですか?

通常、Pythonのリストの先頭に繰り返し追加することは望ましくありません。

それが短くて、あなたがそれをあまりやっていないなら...そしてそれから。

list.insert

list.insertこの方法を使用することができます。

list.insert(0, x)

しかし、これは非効率的です。Pythonでは、a listはポインタの配列であり、Pythonはリスト内のすべてのポインタを取得して1つ下に移動し、最初のスロットにオブジェクトへのポインタを挿入する必要があるため、これは本当に効率的ですあなたが尋ねるように、かなり短いリストのために。

これが実装されているCPythonソースのスニペットを次に示します。ご覧のとおり、配列の最後から開始して、挿入ごとに1つずつ下に移動します。

for (i = n; --i >= where; )
    items[i+1] = items[i];

先頭に要素を追加するのに効率的なコンテナ/リストが必要な場合は、リンクリストが必要です。Pythonには二重にリンクされたリストがあり、最初と最後にすばやく挿入できます。これはと呼ばれますdeque

deque.appendleft

Aにcollections.dequeはリストのメソッドの多くがあります。list.sortは例外であり、deque完全にLiskovをの代わりに使用できるわけではありませんlist

>>> set(dir(list)) - set(dir(deque))
{'sort'}

dequeappendleftメソッドがあります(およびpopleft)。dequeダブルエンドキューと二重リンクリストである-に関係なく長さは、それは常にpreprendの何かに同じ時間がかかります。大きなO表記では、リストのO(1)時間に対するO(1)時間。使い方は次のとおりです。

>>> import collections
>>> d = collections.deque('1234')
>>> d
deque(['1', '2', '3', '4'])
>>> d.appendleft('0')
>>> d
deque(['0', '1', '2', '3', '4'])

deque.extendleft

また、dequeのextendleftメソッドも関連しています。

>>> from collections import deque
>>> d2 = deque('def')
>>> d2.extendleft('cba')
>>> d2
deque(['a', 'b', 'c', 'd', 'e', 'f'])

各要素は一度に1つずつ追加されるので、実際には順序が逆になることに注意してください。

list対のパフォーマンスdeque

最初に、いくつかの反復的なプリペンドを使用してセットアップします。

import timeit
from collections import deque

def list_insert_0():
    l = []
    for i in range(20):
        l.insert(0, i)

def list_slice_insert():
    l = []
    for i in range(20):
        l[:0] = [i]      # semantically same as list.insert(0, i)

def list_add():
    l = []
    for i in range(20):
        l = [i] + l      # caveat: new list each time

def deque_appendleft():
    d = deque()
    for i in range(20):
        d.appendleft(i)  # semantically same as list.insert(0, i)

def deque_extendleft():
    d = deque()
    d.extendleft(range(20)) # semantically same as deque_appendleft above

とパフォーマンス:

>>> min(timeit.repeat(list_insert_0))
2.8267281929729506
>>> min(timeit.repeat(list_slice_insert))
2.5210217320127413
>>> min(timeit.repeat(list_add))
2.0641671380144544
>>> min(timeit.repeat(deque_appendleft))
1.5863927800091915
>>> min(timeit.repeat(deque_extendleft))
0.5352169770048931

dequeははるかに高速です。リストが長くなるにつれ、dequeのパフォーマンスがさらに向上することを期待しています。deque's extendleftを使用できる場合は、おそらくその方法で最高のパフォーマンスが得られます。


57

私のように誰かがこの質問を見つけた場合、提案された方法のパフォーマンステストを次に示します。

Python 2.7.8

In [1]: %timeit ([1]*1000000).insert(0, 0)
100 loops, best of 3: 4.62 ms per loop

In [2]: %timeit ([1]*1000000)[0:0] = [0]
100 loops, best of 3: 4.55 ms per loop

In [3]: %timeit [0] + [1]*1000000
100 loops, best of 3: 8.04 ms per loop

ご覧のとおりinsert、スライスの割り当ては明示的な追加のほぼ2倍の速さで、結果は非常に近いです。以下のようレイモンドヘッティンガーが指摘はinsert、より一般的なオプションであると私は、個人的にリストに付加するこの方法を好みます。


11
このテストで欠けているのは、複雑さです。最初の2つのオプションは一定の複雑さを持っていますが(リスト内の要素が多い場合は遅くなりません)、3番目のオプションは線形の複雑さを持ちます(リスト内の要素の量によっては遅くなります)。リスト全体をコピーする必要があります。リストの要素が増えると、結果はさらに悪化する可能性があります。
ダッカロン

6
@Dakkaron私はあなたがそれについて間違っていると思います。かなりの数のソースがlist.insertの線形複雑さを引用しています。たとえば、この素晴らしいテーブルは、質問者がリンクした合理的な説明によって暗示されています。最初の2つのケースでは、CPythonがリスト内のメモリ内の各要素を再割り当てしているのではないかと思います。したがって、これら3つすべては、おそらく線形の複雑さを持っています。実際にコードを見たりテストしたりしていないので、これらのソースが間違っているとすみません。Collections.deque.appendleftは、あなたが話している線形の複雑さを持っています。
TCプロクター2017

@Dakkaronは真実ではありません。これらはすべて同等の複雑さを持っています。けれども.insert[0:0] = [0]仕事インプレース、彼らはまだ再割り当てバッファ全体に持っています。
juanpa.arrivillaga

これらのベンチマークは悪いです。初期リストは、タイミング自体の一部ではなく、別のセットアップ手順で作成する必要があります。そして最後は1000001の長さの新しいリストを作成するので、他の2つの変異インプレースバージョンと比較すると、リンゴとオレンジです。
WIM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.