web-dev-qa-db-ja.com

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

list.append()はリストの最後に追加するための明らかな選択です。これは 合理的な説明 がないlist.prepend()のためのものです。私のリストが短く、パフォーマンスへの懸念が無視できると仮定すると、

list.insert(0, x)

または

list[0:0] = [x]

慣用句?

446
hurrymaplelad

s.insert(0, x)形式が最も一般的です。

あなたがそれを見るときはいつでも、それはリストの代わりに collections.deque を使うことを考慮するべき時かもしれません。

645

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

new_list = [x] + your_list

もちろん、xyour_listに挿入していないのではなく、xがあらかじめ追加された新しいリストを作成しました。

217
Nil Geisweiller

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

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

それが short であり、あなたがそれをあまりしていないのであれば...それから大丈夫です。

list.insert

list.insertはこのように使用できます。

list.insert(0, x)

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

要素を追加するのに効率的なコンテナが必要な場合は、二重リンクリストが必要です。 Pythonには1つあります - それはdequeと呼ばれます。

deque.appendleft

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

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

dequeには(appendleftと同様に)popleftメソッドもあります。 dequeは両端キューと二重リンクリストです - 長さに関係なく、何かを前処理するのに常に同じ時間がかかります。大きなO記法では、リストのO(1)時間に対するO(n)時間。使い方は次のとおりです。

>>> 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'])

各要素は一度に一つずつ追加されるので、順番を逆にすることに注意してください。

listdequeのパフォーマンス

最初にいくつかの反復的な前置きで設定します。

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のextendleftを使うことができれば、おそらくそのようにして最高のパフォーマンスを得るでしょう。

65
Aaron Hall

誰かが私のようにこの質問を見つけたならば、提案された方法の私のパフォーマンステストはここにあります:

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倍高速で、結果は非常によく似ています。 Raymond Hettinger が述べたようにinsertはより一般的な選択肢であり、私は個人的にはリストの先頭にこの方法を付けるのを好みます。

55

最初のものは確かにはるかに明確で意図をはるかに良く表しています:あなたはリスト全体ではなく単一の要素を挿入したいだけです。

4
Paul Manta