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


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]

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]

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]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.