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

93

Есть ли питонический способ распаковать список в первый элемент и «хвост» в одной команде?

Например:

>> 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, поэтому эта операция является дорогостоящей (например, необходимо скопировать весь список). В зависимости от того, чего вы хотите достичь, это может быть или не быть проблемой. Я просто упомянул об этом, потому что этот тип деструктурирования списка часто встречается в функциональных языках, где на самом деле это очень дешевая операция.
Никлас Б.

Ответы:

192

В 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 - Extended Iterable Unpacking . Это также дает преимущество работы с любыми итерациями, а не только с последовательностями.

Это также действительно читается.

Как описано в 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 Они оба одинаковы - они оба берут элемент головы и создают новый список, содержащий элементы хвоста. Никакой разницы в сложности. Другой класс мог бы лениво реализовать __getitem__/ __setitem__выполнять хвостовую операцию, но встроенный список этого не делает.
Gareth Latty 04
2
В списке из 800 элементов, делающих это 1M раз, у меня 2,8 секунды для головы, решение * tail = seq и только 1,8 секунды для решения head, tail = seq [0], seq [1:]. Для списков нарезка еще быстрее.
Cabu
2
@CMCDragonkai Нет, основной класс списка Python - это список массивов. Это будет O (n), поскольку это включает копирование хвоста в новый список (с одним получением O (1) для головы).
Гарет Латти,
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]
Fraxel
источник
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

Это полезно, когда вы должны перебирать все элементы списка. Например, при наивном слиянии двух разделов в сортировке слияния.

Николай Фоминых
источник
Похоже, что deque (list_instance) имеет сложность O (N). Я ошибся?
Никита Конин 08
1
@ НикитаКонин, вы правы насчет постройки двора. Однако, если вы хотите получить доступ к первому элементу более одного раза, то head, tail = l.popleft(), lэто ~ O (1). head, tail = seq[0], seq[1:]равно O (n).
Николай Фоминых
Похоже, вы можете просто сделать, head = l.popleft()и tailэто просто псевдоним для l. Если lизменения tailизменения тоже.
kon psycho
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]
BobIsNotMyName
источник
1
почему бы вы сделали это вместо того, чтобы просто head, tail = lst[0], lst[1:]? если OP подразумевает использование буквального слова, то он может вручную разделить голову и хвостhead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Филипе Пина
1
(1) Вопрос Op заключался в том, можно ли сделать это в одной строке (поэтому нет lst = ...в предыдущей строке). (2) Выполнение 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]
ABridgeTooFar
источник