Wat is de idiomatische syntaxis voor toevoeging aan een korte pythonlijst?

list.append()is de voor de hand liggende keuze om aan het einde van een lijst toe te voegen. Hier is een redelijke verklaringvoor de ontbrekende list.prepend(). Ervan uitgaande dat mijn lijst kort is en de prestatieproblemen verwaarloosbaar zijn, is

list.insert(0, x)

of

list[0:0] = [x]

idiomatisch?


Antwoord 1, autoriteit 100%

De vorm s.insert(0, x)komt het meest voor.

Als je het echter ziet, is het misschien tijd om te overwegen een collecties te gebruiken. dequein plaats van een lijst.


Antwoord 2, autoriteit 36%

Als je de functionele weg kunt gaan, is het volgende vrij duidelijk

new_list = [x] + your_list

Natuurlijk heb je xniet in your_listingevoegd, maar heb je een nieuwe lijst gemaakt met xervoor.


Antwoord 3, autoriteit 12%

Wat is de idiomatische syntaxis voor toevoeging aan een korte pythonlijst?

Meestal wil je niet herhaaldelijk aan een lijst in Python toevoegen.

Als de lijst kortis, en je doet het niet vaak… ok.

list.insert

De list.insertkan op deze manier worden gebruikt.

list.insert(0, x)

Maar dit is inefficiënt, omdat in Python een listeen reeks aanwijzers is, en Python moet nu elke aanwijzer in de lijst nemen en deze met één naar beneden verplaatsen om de aanwijzer naar uw object in te voegen in het eerste slot, dus dit is eigenlijk alleen efficiënt voor vrij korte lijsten, zoals je vraagt.

Hier is een fragment van de CPython-bronwaar dit is geïmplementeerd – en zoals je kunt zien, beginnen we aan het einde van de array en verplaatsen we alles met één voor elke invoeging:

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

Als je een container/lijst wilt die efficiënt is in het toevoegen van elementen, dan wil je een gekoppelde lijst. Python heeft een dubbel gelinkte lijst, die snel aan het begin en einde kan worden ingevoegd – het wordt een dequegenoemd.

deque.appendleft

Een collections.dequeheeft veel van de methoden van een lijst. list.sortis een uitzondering, waardoor dequedefinitief niet volledig Liskov vervangbaar is voor list.

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

De dequeheeft ook een appendleftmethode (evenals popleft). De dequeis een wachtrij met twee uiteinden en een dubbel gekoppelde lijst – ongeacht de lengte, het kost altijd dezelfde hoeveelheid tijd om iets voor te bereiden. In grote O-notatie, O(1) versus de O(n)-tijd voor lijsten. Dit is het gebruik:

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

deque.extendleft

Ook relevant is de extendleft-methode van de deque, die iteratief voorafgaat:

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

Houd er rekening mee dat elk element één voor één wordt toegevoegd, waardoor de volgorde effectief wordt omgekeerd.

Prestatie van listversus deque

Eerst stellen we in met wat iteratieve toevoegingen:

import timeit
from collections import deque
def list_insert_0(prepends: int):
    l = []
    for i in range(prepends):
        l.insert(0, i)
def list_slice_insert(prepends):
    l = []
    for i in range(prepends):
        l[:0] = [i]      # semantically same as list.insert(0, i)
def list_add(prepends):
    l = []
    for i in range(prepends):
        l = [i] + l      # caveat: new list each time
def deque_appendleft(prepends):
    d = deque()
    for i in range(prepends):
        d.appendleft(i)  # semantically same as list.insert(0, i)
def deque_extendleft(prepends):
    d = deque()
    d.extendleft(range(prepends)) # semantically same as deque_appendleft above

En een functie voor analyse, zodat we alle bewerkingen voor verschillende gebruiksmogelijkheden eerlijk kunnen vergelijken:

def compare_prepends(n, runs_per_trial):
    results = {}
    for function in (
        list_insert_0, list_slice_insert,
        list_add, deque_appendleft, deque_extendleft,
        ):
        shortest_time = min(timeit.repeat(
            lambda: function(n), number=runs_per_trial))
        results[function.__name__] = shortest_time
    ranked_methods = sorted(results.items(), key=lambda kv: kv[1])
    for name, duration in ranked_methods:
        print(f'{name} took {duration} seconds')

en prestaties (het aantal runs per proef verlagen om te compenseren voor langere looptijden of meer prepends – repeatvoert standaard drie proeven uit):

compare_prepends(20, 1_000_000)
compare_prepends(100, 100_000)
compare_prepends(500, 100_000)
compare_prepends(2500, 10_000)
>>> compare_prepends(20, 1_000_000)
deque_extendleft took 0.6490256823599339 seconds
deque_appendleft took 1.4702797569334507 seconds
list_insert_0 took 1.9417422469705343 seconds
list_add took 2.7092894352972507 seconds
list_slice_insert took 3.1809083241969347 seconds
>>> compare_prepends(100, 100_000)
deque_extendleft took 0.1177942156791687 seconds
deque_appendleft took 0.5385235995054245 seconds
list_insert_0 took 0.9471780974417925 seconds
list_slice_insert took 1.4850486349314451 seconds
list_add took 2.1660344172269106 seconds
>>> compare_prepends(500, 100_000)
deque_extendleft took 0.7309095915406942 seconds
deque_appendleft took 2.895373275503516 seconds
list_slice_insert took 8.782583676278591 seconds
list_insert_0 took 8.931685039773583 seconds
list_add took 30.113558700308204 seconds
>>> compare_prepends(2500, 10_000)
deque_extendleft took 0.4839253816753626 seconds
deque_appendleft took 1.5615574326366186 seconds
list_slice_insert took 6.712615916505456 seconds
list_insert_0 took 13.894083382561803 seconds
list_add took 72.1727528590709 seconds

De deque is veel sneller. Naarmate de lijsten langer worden, presteren deques nog beter. Als je extendleftvan deque kunt gebruiken, krijg je op die manier waarschijnlijk de beste prestaties.

Als u lijsten moet gebruiken, houd er dan rekening mee dat voor kleine lijsten list.insertsneller werkt, maar voor grotere lijsten wordt het invoegen met plaknotatie sneller.

Niet toevoegen aan lijsten

Lijsten waren bedoeld om aan te worden toegevoegd, niet om eraan te worden toegevoegd. Als je een situatie hebt waarin dit soort prepending de prestaties van je code schaadt, schakel dan over naar een deque of, als je je semantiek kunt omkeren en hetzelfde doel kunt bereiken, draai je lijst om en voeg in plaats daarvan toe.

Vermijd in het algemeen de toevoeging aan het ingebouwde Python list-object.


Antwoord 4, autoriteit 6%

Als iemand deze vraag net als ik vindt, zijn hier mijn prestatietests van voorgestelde methoden:

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

Zoals je kunt zien, zijn inserten slice-toewijzing bijna twee keer zo snel als expliciet toevoegen en komen ze zeer dicht in de buurt van de resultaten. Aangezien Raymond Hettingeropmerkte dat insertvaker voorkomt en ik persoonlijk liever op deze manier voeg lijst.


Antwoord 5

Naar mijn mening is de meest elegante en idiomatische manier om een ​​element of lijst aan een andere lijst toe te voegen, in Python, het gebruik van de uitbreidingsoperator * (ook wel de uitpakoperator genoemd),

# Initial list
l = [4, 5, 6]
# Modification
l = [1, 2, 3, *l]

Waarbij de resulterende lijst na de wijziging [1, 2, 3, 4, 5, 6]

Ik vind het ook leuk om gewoon twee lijsten te combineren met de operator +, zoals weergegeven,

# Prepends [1, 2, 3] to l
l = [1, 2, 3] + l
# Prepends element 42 to l
l = [42] + l

Ik hou niet van de andere algemene benadering, l.insert(0, value), omdat er een magisch getal voor nodig is. Bovendien staat insert()alleen het toevoegen van een enkel element toe, maar de bovenstaande benadering heeft dezelfde syntaxis voor het toevoegen van een enkel element of meerdere elementen.


Antwoord 6

Laten we 4 methoden bespreken

  1. Invoegen() gebruiken
>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l.insert(0, 5)
>>> l
[5, 0, 1, 2, 3, 4]
>>> 
  1. Met [] en +
>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l = [5] + l
>>> l
[5, 0, 1, 2, 3, 4]
>>> 
  1. Snijden gebruiken
>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l[:0] = [5]
>>> l
[5, 0, 1, 2, 3, 4]
>>> 
  1. collections.deque.appendleft() gebruiken
>>> 
>>> from collections import deque
>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l = deque(l)
>>> l.appendleft(5)
>>> l = list(l)
>>> l
[5, 0, 1, 2, 3, 4]
>>> 

Other episodes