Kopf und Schwanz in einer Linie


88

Gibt es eine pythonische Möglichkeit, eine Liste im ersten Element und den "Schwanz" in einem einzigen Befehl zu entpacken?

Beispielsweise:

>> 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
Denken Sie daran, dass Listen in Python nicht als einfach verknüpfte Listen implementiert sind. Daher ist dieser Vorgang kostspielig (wie in: Die gesamte Liste muss kopiert werden). Je nachdem, was Sie erreichen möchten, kann dies ein Problem sein oder auch nicht. Ich erwähne das nur, weil diese Art der Listendestrukturierung oft in funktionalen Sprachen zu finden ist, wo es sich tatsächlich um eine sehr billige Operation handelt.
Niklas B.

Antworten:


187

Unter Python 3.x können Sie dies gut tun:

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

Eine neue Funktion in 3.x ist die Verwendung des *Operators beim Auspacken, um zusätzliche Werte zu bezeichnen. Es ist in PEP 3132 - Erweitertes iterierbares Auspacken beschrieben . Dies hat auch den Vorteil, dass an iterierbaren Sequenzen gearbeitet werden kann, nicht nur an Sequenzen.

Es ist auch sehr gut lesbar.

Wie im PEP beschrieben, müssen Sie Folgendes tun, wenn Sie das Äquivalent unter 2.x ausführen möchten (ohne möglicherweise eine temporäre Liste zu erstellen):

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

Wie in den Kommentaren erwähnt, bietet dies auch die Möglichkeit, einen Standardwert für zu erhalten, headanstatt eine Ausnahme auszulösen. Wenn Sie dieses Verhalten wünschen, verwenden Sie next()ein optionales zweites Argument mit einem Standardwert. Wenn next(it, None)Sie Nonealso kein head-Element hätten, würden Sie dies angeben.

Wenn Sie an einer Liste arbeiten, ist der einfachste Weg ohne die 3.x-Syntax natürlich:

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

1
Entschuldigung, ich habe den Begriff Schwanz nicht richtig verwendet. Ich meine, was ich im Beispiel sage, das ist die Liste ohne das erste Element
Giacomo d'Antonio

1
@NikolayFominyh Sie sind beide gleich - beide nehmen das Kopfelement und erstellen eine neue Liste mit den Endelementen. Kein Unterschied in der Komplexität. Eine andere Klasse könnte die Endoperation träge implementieren __getitem__/ __setitem__ausführen, die eingebaute Liste jedoch nicht.
Gareth Latty

2
Auf einer Liste von 800 Elementen, die es 1M-mal machen, habe ich 2,8s für die Lösung head, * tail = seq und nur 1,8s für head, tail = seq [0], seq [1:] solution. Das Schneiden ist für Listen immer noch schneller.
Cabu

2
@CMCDragonkai Nein, Pythons Hauptlistenklasse ist eine Array-Liste. Dies wäre O (n), da der Schwanz in eine neue Liste kopiert wird (mit einem O (1) für den Kopf).
Gareth Latty

1
elegante Lösung, schöne Python ;-)
Noleto

35
>>> 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

Für die Komplexität der O (1) -Operation head,tailsollten Sie jedoch verwenden deque.

Folgender Weg:

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

Dies ist nützlich, wenn Sie alle Elemente der Liste durchlaufen müssen. Zum Beispiel beim naiven Zusammenführen von 2 Partitionen in der Zusammenführungssortierung.


Es scheint, dass deque (list_instance) eine O (N) -Komplexität hat. Liege ich falsch?
8кита Конин

1
@ НикитаКонин, Sie haben Recht mit dem Bau von Deque. Wenn Sie jedoch mehrmals auf das erste Element zugreifen möchten, head, tail = l.popleft(), llautet ~ O (1). head, tail = seq[0], seq[1:]ist O (n).
Nikolay Fominyh

Es sieht so aus, als ob Sie es einfach tun können head = l.popleft()und tailist nur ein Alias ​​für l. Wenn sich auch lÄnderungen tailändern.
Kon Psych

2

Python 2 mit 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
warum in der Welt würden Sie dies tun , anstatt nur head, tail = lst[0], lst[1:]? Wenn OP bedeutet, ein Literal zu verwenden, könnte er Kopf und Schwanz manuell teilenhead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina

1
(1) Die Frage von Op war, ob dies in einer Zeile möglich ist (also nein lst = ...in der vorherigen Zeile). (2) Wenn Sie dies tun, head, tail = lst[0], lst[1:]bleibt der Code für Nebenwirkungen offen (berücksichtigen Sie head, tail = get_list()[0], get_list()[1:]) und unterscheidet sich von der Form von Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName

Davon abgesehen erkenne ich an, dass dies ein schlecht verschleierter Weg ist, um den Kopf / Schwanz zu bekommen. Aber ich dachte, es wäre die beste Antwort für Python 2 auf Op's spezifische Frage.
BobIsNotMyName

1

Aufbauend auf der Python 2-Lösung von @GarethLatty können Sie im Folgenden eine einzelne Zeile ohne Zwischenvariablen in Python 2 erhalten.

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

Wenn es ausnahmesicher sein soll (dh leere Liste unterstützt), fügen Sie Folgendes hinzu:

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

Wenn Sie es ohne das Semikolon tun möchten, verwenden Sie:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.