Cabeça e cauda em uma linha


90

Existe uma maneira pythônica de desempacotar uma lista no primeiro elemento e a "cauda" em um único comando?

Por exemplo:

>> 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
Lembre-se de que as listas não são implementadas como listas com links simples em Python, portanto, essa operação é cara (como em: a lista inteira precisa ser copiada). Dependendo do que você deseja alcançar, isso pode ou não ser um problema. Estou apenas mencionando isso porque esse tipo de desestruturação de lista é freqüentemente encontrado em linguagens funcionais, onde na verdade é uma operação muito barata.
Niklas B.

Respostas:


189

No Python 3.x, você pode fazer isso muito bem:

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

Um novo recurso no 3.x é usar o *operador na descompactação, para significar quaisquer valores extras. É descrito em PEP 3132 - Desempacotamento Iterável Estendido . Isso também tem a vantagem de trabalhar em qualquer iterável, não apenas em sequências.

Também é muito legível.

Conforme descrito no PEP, se você quiser fazer o equivalente em 2.x (sem potencialmente fazer uma lista temporária), você deve fazer o seguinte:

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

Conforme observado nos comentários, isso também fornece uma oportunidade de obter um valor padrão para, em headvez de lançar uma exceção. Se você deseja este comportamento, next()recebe um segundo argumento opcional com um valor padrão, então next(it, None)forneceria Nonese não houvesse elemento head.

Naturalmente, se você estiver trabalhando em uma lista, a maneira mais fácil sem a sintaxe 3.x é:

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

1
desculpe, usei o termo cauda incorretamente. Quero dizer o que digo no exemplo, que é a lista sem o primeiro elemento
Giacomo d'Antonio

1
@NikolayFominyh Ambos são iguais - ambos pegam o elemento principal e constroem uma nova lista contendo os elementos finais. Nenhuma diferença na complexidade. Outra classe poderia implementar __getitem__/ __setitem__para fazer a operação final preguiçosamente, mas a lista interna não o faz.
Gareth Latty

2
Em uma lista de 800 elementos fazendo isso 1M vezes, tenho 2.8s para a cabeça, * cauda = solução seq e apenas 1.8s para a cabeça, cauda = seq [0], solução seq [1:]. O fatiamento é ainda mais rápido para listas.
Cabu,

2
@CMCDragonkai Não, a principal classe de lista do Python é uma lista de array. Isso seria O (n), pois envolve copiar a cauda para uma nova lista (com um O (1) get para a cabeça).
Gareth Latty

1
Esta bela sintaxe é outro motivo para mudar parapython 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

No entanto, para complexidade de head,tailoperação O (1) , você deve usar deque.

Caminho seguinte:

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

É útil quando você deve iterar por todos os elementos da lista. Por exemplo, na mesclagem ingênua de 2 partições na classificação de mesclagem.


Parece que deque (list_instance) tem complexidade O (N). Estou errado?
Никита Конин

1
@ НикитаКонин, você está certo sobre a construção de deque. No entanto, se você deseja acessar o primeiro elemento mais de uma vez, head, tail = l.popleft(), lé ~ O (1). head, tail = seq[0], seq[1:]é O (n).
Nikolay Fominyh

Parece que você pode apenas fazer head = l.popleft()e tailé apenas um apelido para l. Se las tailmudanças também mudarem.
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 que no mundo você faria isso em vez de apenas head, tail = lst[0], lst[1:]? se OP significa usar um literal então ele poderia dividir cabeça e cauda manualmentehead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina

1
(1) A pergunta de Op era se é possível fazer isso em uma linha (portanto, não lst = ...na linha anterior). (2) Fazer head, tail = lst[0], lst[1:]deixa o código aberto a efeitos colaterais (considere head, tail = get_list()[0], get_list()[1:]) e é diferente da forma de Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName

Dito isso, reconheço que esta é uma maneira mal ofuscada de obter a cabeça / cauda. Mas eu pensei que era a melhor resposta para Python 2 para a pergunta específica de Op.
BobIsNotMyName

1

Com base na solução Python 2 de @GarethLatty , o seguinte é uma maneira de obter um equivalente de linha única sem variáveis ​​intermediárias em Python 2.

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

Se você precisa que seja à prova de exceção (ou seja, suporte para lista vazia), adicione:

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

Se você quiser fazer isso sem o ponto-e-vírgula, use:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.