Голова і хвіст в одну лінію


91

Чи існує пітонічний спосіб розпакувати список у першому елементі та "хвіст" за допомогою однієї команди?

Наприклад:

>> 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
Пам’ятайте, що списки в Python не реалізуються як списки, що мають єдине зв’язування, тому ця операція коштує дорого (як у: весь список потрібно скопіювати). Залежно від того, чого ви хочете досягти, це може чи не може бути проблемою. Я лише згадую про це, оскільки такий тип деструктуризації списків часто зустрічається у функціональних мовах, де насправді це дуже дешева операція.
Ніклас Б.

Відповіді:


191

Під Python 3.x ви можете зробити це чудово:

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

Нова функція в 3.x полягає у використанні *оператора при розпаковуванні, що означає будь-які зайві значення. Це описано в PEP 3132 - Розширене розпаковування, що підлягає ітерації . Це також має перевагу в роботі над будь-якими ітераційними, а не лише послідовностями.

Це також справді читається.

Як описано в PEP, якщо ви хочете зробити еквівалент під 2.x (без потенційного складання тимчасового списку), вам потрібно зробити це:

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

Як зазначалося у коментарях, це також надає можливість отримати значення за замовчуванням, headа не викидати виняток. Якщо ви хочете такої поведінки, next()приймає необов'язковий другий аргумент зі значенням за замовчуванням, тому next(it, None)дасть вам, Noneякщо не було елемента head.

Звичайно, якщо ви працюєте над списком, найпростіший спосіб без синтаксису 3.x:

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

1
вибачте, я неправильно вжив термін хвіст. Я маю на увазі те, що я кажу в прикладі, тобто список без першого елемента
Джакомо д'Антоніо

1
@NikolayFominyh Вони обидва однакові - обидва беруть елемент head і створюють новий список, що містить елементи хвоста. Ніякої різниці в складності. Інший клас міг би реалізувати __getitem__/ __setitem__виконувати операцію хвоста ліниво, але вбудований список цього не робить.
Gareth Latty

2
У списку з 800 елементів, що роблять це 1 мільйон разів, у мене є 2.8s для голови, * tail = seq рішення і лише 1.8s для head, tail = seq [0], seq [1:] рішення. Нарізка все ще швидша для списків.
Кабу,

2
@CMCDragonkai Ні, основним класом списку Python є список масивів. Це буде O (n), оскільки передбачає копіювання хвоста в новий список (з одним O (1), отриманим за голову).
Gareth Latty

1
Цей приємний синтаксис - ще одна причина перейти доpython 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

Однак для складності head,tailоперації O (1) вам слід скористатися deque.

Наступним способом:

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

Це корисно, коли ви повинні переглядати всі елементи списку. Наприклад, при наївному об'єднанні 2 розділів у режимі сортування.


Здається, deque (list_instance) має складність O (N). Я помиляюся?
Никита Конин

1
@ НикитаКонин, ти маєш рацію щодо побудови деке. Однак, якщо ви хочете отримати доступ до першого елемента більше одного разу, head, tail = l.popleft(), lце ~ O (1). head, tail = seq[0], seq[1:]дорівнює O (n).
Микола Фоміних

Схоже, ви можете просто зробити head = l.popleft()і tailє просто псевдонімом для l. Якщо lзміни tailзміни теж.
kon psych

2

Python 2, використовуючи лямбду

>>> 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
чому б у світі ви робили це, а не просто head, tail = lst[0], lst[1:]? якщо ОП означає використовувати літерал, тоді він міг би розділити голову та хвіст вручнуhead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Філіпе Піна

1
(1) Питання Опа полягало в тому, чи можна це зробити в один рядок (отже, ні lst = ...в попередньому рядку). (2) Doing head, tail = lst[0], lst[1:]залишає код відкритим для побічних ефектів (розглянемо head, tail = get_list()[0], get_list()[1:]) і відрізняється від форми Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName

З огляду на це, я визнаю, що це поганий затуманений спосіб отримати голову / хвіст. Але я вважав, що це найкраща відповідь на Python 2 на конкретне питання Op.
BobIsNotMyName

1

Спираючись на рішення Python 2 від @GarethLatty , далі наведено спосіб отримати однорядковий еквівалент без проміжних змінних у Python 2.

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

Якщо вам потрібно, щоб він був захищений від винятків (тобто підтримував порожній список), додайте:

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

Якщо ви хочете зробити це без крапки з комою, використовуйте:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.