Kısa bir python listesine geçmenin deyimsel sözdizimi nedir?


543

list.append()listenin sonuna eklemek için bariz bir seçimdir. İşte kayıplar için makul bir açıklamalist.prepend() . Listemin kısa ve performans kaygılarının ihmal edilebilir olduğunu varsayarsak,

list.insert(0, x)

veya

list[0:0] = [x]

deyimsel?

Yanıtlar:


784

s.insert(0, x)Form en yaygın olanıdır.

Yine de her gördüğünüzde liste yerine collections.deque kullanmayı düşünebilirsiniz .


9
"Yine de her gördüğünüzde, bir liste yerine bir collections.deque kullanmayı düşünebilirsiniz." Bu neden?
Matt M.

6
@MattM. Bir listenin önüne eklerseniz, python diğer tüm öğeleri bir boşluk ileri taşımak zorunda kalır, listeler "önde boşluk bırakamaz". collections.deque (çift uçlu kuyruk) "önden alan açmak" için desteğe sahiptir ve bu durumda çok daha hızlıdır.
fejfo

265

İşlevsel yoldan gidebilirseniz, aşağıdakiler oldukça açıktır

new_list = [x] + your_list

Tabii ki sen takılı değil xiçine your_listziyade sizinle yeni bir liste oluşturduk, xbuna preprended.


45
Gözlemlediğiniz gibi, bu bir listeye eklenmez. Yeni bir liste oluşturuyor. Bu yüzden soruyu hiç tatmin etmiyor.
Chris Morgan

112
Soruyu tatmin etmese de, yuvarlar ve bu web sitesinin amacı budur. Yorumu takdir edin ve haklısınız, ancak insanlar bunu aradığında bunu görmek yardımcı olur.
dave4jr

2
Ayrıca, listeyi bir listeye eklemek istiyorsanız insert'i kullanmak beklendiği gibi çalışmaz. ama bu yöntem var!
gota

89

Kısa bir python listesine geçmenin deyimsel sözdizimi nedir?

Genellikle Python'daki bir listeye tekrar tekrar eklemek istemezsiniz.

E? Er kısa ve onu çok yapmıyoruz ... Sonra Tamam.

list.insert

Bu list.insertşekilde kullanılabilir.

list.insert(0, x)

Ancak bu verimsizdir, çünkü Python'da a listbir işaretçi dizisidir ve Python şimdi listedeki her işaretçiyi almalı ve işaretçiyi ilk yuvaya nesnenize eklemek için birer birer aşağı taşımalıdır, bu yüzden bu gerçekten etkilidir istediğin gibi kısa listeler için.

İşte bunun uygulandığı CPython kaynağından bir pasaj - ve gördüğünüz gibi, dizinin sonunda başlıyoruz ve her ekleme için her şeyi birer birer aşağı taşıyoruz:

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

Önleyen öğelerde etkili olan bir kapsayıcı / liste istiyorsanız, bağlantılı bir liste istersiniz. Python'un başında ve sonunda hızlı bir şekilde eklenebilen çift bağlantılı bir liste var - buna a denir deque.

deque.appendleft

A collections.deque, bir liste yönteminin çoğuna sahiptir. list.sortbir istisna, dequekesinlikle Liskov'un yerine geçemez list.

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

dequeAynı zamanda bir yer alır appendleftyöntemi (aynı zamanda popleft). Bu deque, çift uçlu bir kuyruk ve çift bağlantılı bir listedir - uzunluğu ne olursa olsun, bir şeyi önceden eklemek her zaman aynı zaman alır. Büyük O gösterimlerinde, listeler için O (1) ile O (n) arasındaki zaman. İşte kullanımı:

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

deque.extendleft

Ayrıca extendleft, yinelemeli olarak tercih edilen deque'nin yöntemi de geçerlidir :

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

Her bir öğenin birer birer ekleneceğini ve böylece siparişlerini etkili bir şekilde tersine çevireceğini unutmayın.

listKarşı performansdeque

Öncelikle, tekrarlayan bazı yinelemeli kurulumlar yapıyoruz:

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

ve performans:

>>> 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 çok daha hızlı. Listeler uzadıkça, bir deque'nin daha iyi performans göstermesini beklerdim. Deque'leri kullanabiliyorsanız extendleftmuhtemelen bu şekilde en iyi performansı elde edersiniz.


57

Birisi bu soruyu benim gibi bulursa, önerilen yöntemlerin performans testleri:

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

Gördüğünüz gibi, insertdilim ataması, açık eklemeden neredeyse iki kat daha hızlıdır ve sonuçlara çok yakındır. Raymond Hettinger'in belirttiği gibi insertdaha yaygın bir seçenektir ve ben kişisel olarak bu listeyi tercih etmeyi tercih ederim.


11
Bu testte eksik olan bir şey karmaşıklıktır. İlk iki seçenek sürekli karmaşıklığa sahipken (listede daha fazla öğe olduğunda daha yavaş olmaz), üçüncüsünde doğrusal karmaşıklık vardır (listedeki öğelerin miktarına bağlı olarak yavaşlar), çünkü her zaman listenin tamamını kopyalamak zorundadır. Listede daha fazla öğe olduğunda, sonuç çok daha kötü olabilir.
Dakkaron

6
@Dakkaron Bence bu konuda yanılıyorsun. Oldukça az sayıda kaynak list.insert, örneğin bu güzel tablo için doğrusal karmaşıklıktan bahseder ve sorgulayıcının bağlı olduğu makul açıklama ile ima edilir. CPython'un listedeki bellekteki her bir öğeyi ilk iki durumda yeniden tahsis ettiğinden şüpheleniyorum, bu nedenle bunların üçü de muhtemelen doğrusal karmaşıklığa sahip. Aslında koda bakmadım ya da kendim test ettim, bu kaynaklar yanlışsa çok üzgünüm. Collections.deque.appendleft, bahsettiğiniz doğrusal karmaşıklığa sahiptir.
TC Proctor

@Dakkaron doğru değil, hepsinin eşdeğer karmaşıklığı var. Her ne kadar .insertve yerinde[0:0] = [0] çalışırlarsa da , yine de tüm tamponu yeniden tahsis etmek zorundadırlar.
juanpa.arrivillaga

Bu kriterler kötü. İlk liste, zamanlamanın kendisinin bir parçası değil, ayrı kurulum adımında oluşturulmalıdır. Ve son 1000001 uzunluğunda yeni bir liste oluşturur, bu yüzden diğer iki mutasyona uğramış yerinde versiyonla karşılaştırıldığında elma ve portakal.
Ocak'ta
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.