Testa e coda in una riga


90

Esiste un modo pitonico per scompattare una lista nel primo elemento e la "coda" in un singolo comando?

Per esempio:

>> head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

9
Ricorda che gli elenchi non sono implementati come elenchi collegati singolarmente in Python, quindi questa operazione è costosa (come in: l'intero elenco deve essere copiato). A seconda di ciò che si desidera ottenere, questo potrebbe o meno essere un problema. Lo dico solo perché questo tipo di destrutturazione delle liste si trova spesso nei linguaggi funzionali, dove in realtà è un'operazione molto economica.
Niklas B.

Risposte:


189

In Python 3.x, puoi farlo bene:

>>> head, *tail = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

Una nuova funzionalità in 3.x consiste nell'utilizzare l' *operatore durante il disimballaggio, per indicare eventuali valori aggiuntivi. È descritto in PEP 3132 - Extended Iterable Unpacking . Questo ha anche il vantaggio di lavorare su qualsiasi iterabile, non solo su sequenze.

È anche molto leggibile.

Come descritto nel PEP, se vuoi fare l'equivalente in 2.x (senza potenzialmente creare un elenco temporaneo), devi farlo:

it = iter(iterable)
head, tail = next(it), list(it)

Come notato nei commenti, questo fornisce anche l'opportunità di ottenere un valore predefinito per headinvece di generare un'eccezione. Se vuoi questo comportamento, next()accetta un secondo argomento opzionale con un valore predefinito, quindi next(it, None)ti darebbe Nonese non ci fosse un elemento head.

Naturalmente, se stai lavorando su un elenco, il modo più semplice senza la sintassi 3.x è:

head, tail = seq[0], seq[1:]

1
scusa, ho usato il termine coda in modo non corretto. Intendo quello che dico nell'esempio, cioè la lista senza il primo elemento
Giacomo d'Antonio

1
@NikolayFominyh Sono entrambi uguali: entrambi prendono l'elemento head e costruiscono un nuovo elenco contenente gli elementi tail. Nessuna differenza di complessità. Un'altra classe potrebbe implementare __getitem__/ __setitem__per eseguire pigramente l'operazione tail, ma l'elenco predefinito non lo fa.
Gareth Latty

2
Su un elenco di 800 elementi che lo fanno 1M volte, ho 2.8s per la soluzione head, * tail = seq e solo 1.8s per head, tail = seq [0], seq [1:] solution. Il sezionamento è ancora più veloce per gli elenchi.
Cabu

2
@CMCDragonkai No, la classe della lista principale di Python è una lista di array. Questo sarebbe O (n) in quanto implica la copia della coda in una nuova lista (con un O (1) get per la testa).
Gareth Latty

1
Questa bella sintassi è un altro motivo per passare apython 3.x
eigenfield

36
>>> mylist = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>> head, tail = mylist[0], mylist[1:]
>>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

9

Tuttavia, per la complessità head,tailoperativa O (1) è necessario utilizzare deque.

Modo seguente:

from collections import deque
l = deque([1,2,3,4,5,6,7,8,9])
head, tail = l.popleft(), l

È utile quando è necessario scorrere tutti gli elementi dell'elenco. Ad esempio in ingenuo unire 2 partizioni in merge sort.


Sembra che deque (list_instance) abbia una complessità O (N). Ho sbagliato?
Никита Конин

1
@ НикитаКонин, hai ragione sulla costruzione di deque. Tuttavia, se vuoi accedere al primo elemento più di una volta, allora head, tail = l.popleft(), lè ~ O (1). head, tail = seq[0], seq[1:]è O (n).
Nikolay Fominyh

Sembra che tu possa semplicemente fare head = l.popleft()ed tailè solo un alias per l. Se lcambia tailcambia troppo.
kon psych

2

Python 2, usando lambda

>>> head, tail = (lambda lst: (lst[0], lst[1:]))([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
>>> head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

1
perché nel mondo lo faresti invece di solo head, tail = lst[0], lst[1:]? se OP significa usare un letterale, allora potrebbe dividere testa e coda manualmentehead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina

1
(1) La domanda di Op era se fosse possibile farlo in una riga (quindi no lst = ...nella riga precedente). (2) Fare head, tail = lst[0], lst[1:]lascia il codice aperto agli effetti collaterali (considera head, tail = get_list()[0], get_list()[1:]) ed è diverso dalla forma di Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName

Detto questo, riconosco che questo è un cattivo modo offuscato per ottenere la testa / coda. Ma ho pensato che fosse la migliore risposta per Python 2 alla domanda specifica di Op.
BobIsNotMyName

1

Basandosi sulla soluzione Python 2 di @GarethLatty , il seguente è un modo per ottenere una singola riga equivalente senza variabili intermedie in Python 2.

t=iter([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]);h,t = [(h,list(t)) for h in t][0]

Se hai bisogno che sia a prova di eccezioni (cioè che supporti un elenco vuoto), aggiungi:

t=iter([]);h,t = ([(h,list(t)) for h in t]+[(None,[])])[0]

Se vuoi farlo senza il punto e virgola, usa:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.