Głowa i ogon w jednej linii


92

Czy istnieje pythonowy sposób na rozpakowanie listy w pierwszym elemencie i „ogonie” w pojedynczym poleceniu?

Na przykład:

>> 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
Pamiętaj, że listy w Pythonie nie są implementowane jako listy połączone pojedynczo, więc ta operacja jest kosztowna (jak w przypadku: cała lista musi zostać skopiowana). W zależności od tego, co chcesz osiągnąć, może to stanowić problem lub nie. Wspominam tylko o tym, ponieważ ten rodzaj destrukcji list jest często spotykany w językach funkcjonalnych, gdzie jest w rzeczywistości bardzo tanią operacją.
Niklas B.

Odpowiedzi:


192

W Pythonie 3.x możesz to ładnie zrobić:

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

Nowością w 3.x jest użycie *operatora podczas rozpakowywania, co oznacza wszelkie dodatkowe wartości. Jest to opisane w PEP 3132 - Extended Iterable Unpacking . Ma to również tę zaletę, że pracuje nad dowolnymi iterowalnymi, nie tylko sekwencjami.

Jest też bardzo czytelny.

Jak opisano w PEP, jeśli chcesz zrobić odpowiednik w wersji 2.x (bez potencjalnie tworzenia tymczasowej listy), musisz to zrobić:

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

Jak zauważono w komentarzach, daje to również możliwość uzyskania wartości domyślnej headzamiast zgłaszania wyjątku. Jeśli chcesz tego zachowania, next()pobiera opcjonalny drugi argument z wartością domyślną, więc next(it, None)dałby ci, Nonegdyby nie było elementu head.

Oczywiście, jeśli pracujesz nad listą, najłatwiejszym sposobem bez składni 3.x jest:

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

1
przepraszam, niepoprawnie użyłem terminu ogon. Chodzi mi o to, co powiem w przykładzie, czyli lista bez pierwszego elementu
Giacomo d'Antonio

1
@NikolayFominyh Oba są takie same - obaj biorą element head i konstruują nową listę zawierającą elementy tail. Bez różnicy w złożoności. Inna klasa mogłaby leniwie zaimplementować __getitem__/ __setitem__wykonać ogonową operację, ale wbudowana lista nie.
Gareth Latty,

2
Na liście 800 elementów wykonujących to 1 mln razy mam 2,8s na głowę, * tail = rozwiązanie seq i tylko 1,8s na głowę, tail = seq [0], seq [1:] rozwiązanie. W przypadku list krojenie jest nadal szybsze.
Cabu,

2
@CMCDragonkai Nie, główna klasa listy Pythona to lista tablic. Byłoby to O (n), ponieważ polega na skopiowaniu ogona do nowej listy (z jednym O (1) uzyskanym za głowę).
Gareth Latty

1
Ta ładna składnia to kolejny powód, aby przejść dopython 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

Jednak ze względu na złożoność head,tailoperacji O (1) należy użyć deque.

Podążać drogą:

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

Jest to przydatne, gdy musisz iterować przez wszystkie elementy listy. Na przykład w naiwnym łączeniu 2 partycji w sortowaniu przez scalanie.


Wygląda na to, że deque (list_instance) ma złożoność O (N). Czy się mylę?
Никита Конин

1
@ НикитаКонин, masz rację co do budowy deque. Jeśli jednak chcesz uzyskać dostęp do pierwszego elementu więcej niż raz, to head, tail = l.popleft(), ljest ~ O (1). head, tail = seq[0], seq[1:]jest O (n).
Nikolay Fominyh

Wygląda na to, że możesz to zrobić head = l.popleft()i tailjest to tylko alias dla l. Jeśli lchanges tailzmiany too.
kon psych

2

Python 2, używając 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
dlaczego na świecie miałbyś to robić zamiast po prostu head, tail = lst[0], lst[1:]? jeśli OP oznacza użycie dosłownego, mógłby ręcznie rozdzielić głowę i ogonhead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina

1
(1) Pytanie operatora brzmiało, czy można to zrobić w jednej linii (więc nie lst = ...w poprzedniej linii). (2) Wykonanie head, tail = lst[0], lst[1:]pozostawia kod otwarty na skutki uboczne (rozważ head, tail = get_list()[0], get_list()[1:]) i różni się od formy Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName

Biorąc to pod uwagę, przyznaję, że jest to zły, zaciemniony sposób na zdobycie głowy / ogona. Ale pomyślałem, że to najlepsza odpowiedź dla Pythona 2 na konkretne pytanie Op.
BobIsNotMyName

1

Opierając się na rozwiązaniu Python 2 z @GarethLatty , poniżej przedstawiono sposób uzyskania odpowiednika w jednym wierszu bez zmiennych pośrednich w Pythonie 2.

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

Jeśli potrzebujesz, aby był odporny na wyjątki (tj. Obsługujący pustą listę), dodaj:

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

Jeśli chcesz to zrobić bez średnika, użyj:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.