Cabeza y cola en una línea


93

¿Existe una forma pitónica de descomprimir una lista en el primer elemento y la "cola" en un solo comando?

Por ejemplo:

>> 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
Recuerde que las listas no se implementan como listas de enlaces individuales en Python, por lo que esta operación es costosa (como en: es necesario copiar la lista completa). Dependiendo de lo que desee lograr, esto puede ser un problema o no. Solo menciono eso porque este tipo de desestructuración de listas se encuentra a menudo en lenguajes funcionales, donde en realidad es una operación muy barata.
Niklas B.

Respuestas:


192

En Python 3.x, puede hacer esto muy bien:

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

Una nueva característica en 3.x es usar el *operador al desempacar, para referirse a cualquier valor adicional. Se describe en PEP 3132 - Desembalaje iterable extendido . Esto también tiene la ventaja de trabajar en cualquier iterable, no solo en secuencias.

También es muy legible.

Como se describe en el PEP, si desea hacer el equivalente en 2.x (sin potencialmente hacer una lista temporal), debe hacer esto:

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

Como se señaló en los comentarios, esto también brinda la oportunidad de obtener un valor predeterminado en headlugar de lanzar una excepción. Si desea este comportamiento, next()toma un segundo argumento opcional con un valor predeterminado, por next(it, None)lo que se lo daría Nonesi no hubiera un elemento principal.

Naturalmente, si está trabajando en una lista, la forma más sencilla sin la sintaxis 3.x es:

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

1
lo siento, utilicé el término cola de manera incorrecta. Me refiero a lo que digo en el ejemplo, que es la lista sin el primer elemento
Giacomo d'Antonio

1
@NikolayFominyh Ambos son iguales: ambos toman el elemento principal y construyen una nueva lista que contiene los elementos finales. No hay diferencia de complejidad. Otra clase podría implementar __getitem__/ __setitem__para hacer la operación de cola de manera perezosa, pero la lista incorporada no lo hace.
Gareth Latty

2
En una lista de 800 elementos haciéndolo 1M veces, tengo 2.8s para la solución head, * tail = seq solution y solo 1.8s para head, tail = seq [0], seq [1:] solution. Cortar es aún más rápido para las listas.
Cabu

2
@CMCDragonkai No, la clase de lista principal de Python es una lista de matriz. Esto sería O (n) ya que implica copiar la cola a una nueva lista (con un O (1) obtener para la cabeza).
Gareth Latty

1
Esta agradable sintaxis es otra razón para pasar apython 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

Sin embargo, para la complejidad de head,tailoperación O (1) debe utilizar deque.

Siguiente manera:

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

Es útil cuando debe recorrer todos los elementos de la lista. Por ejemplo, en la fusión ingenua de 2 particiones en ordenación por fusión.


Parece que deque (list_instance) tiene complejidad O (N). ¿Me equivoco?
Никита Конин

1
@ НикитаКонин, tienes razón sobre la construcción de deque. Sin embargo, si desea acceder al primer elemento más de una vez, entonces head, tail = l.popleft(), les ~ O (1). head, tail = seq[0], seq[1:]Está encendido).
Nikolay Fominyh

Parece que puedes hacerlo head = l.popleft()y tailes solo un alias para l. Si lcambia tailtambién cambia.
kon psych

2

Python 2, usando 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
¿Por qué demonios harías esto en lugar de solo head, tail = lst[0], lst[1:]? si OP significa usar un literal, entonces podría dividir la cabeza y la cola manualmentehead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina

1
(1) La pregunta de Op era si es posible hacer esto en una línea (así que no lst = ...en la línea anterior). (2) Doing head, tail = lst[0], lst[1:]deja el código abierto a efectos secundarios (considerar head, tail = get_list()[0], get_list()[1:]), y es diferente a la forma de Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName

Dicho esto, reconozco que esta es una forma mal ofuscada de conseguir la cabeza / cola. Pero pensé que era la mejor respuesta para Python 2 para la pregunta específica de Op.
BobIsNotMyName

1

Sobre la base de la solución Python 2 de @GarethLatty , la siguiente es una forma de obtener un equivalente de una sola línea sin variables intermedias en Python 2.

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

Si necesita que sea a prueba de excepciones (es decir, que admita una lista vacía), agregue:

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

Si quieres hacerlo sin el punto y coma, usa:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.