Tête et queue en une seule ligne

90

Existe-t-il un moyen pythonique de décompresser une liste dans le premier élément et la "queue" en une seule commande?

Par exemple:

>> 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]
Giacomo d'Antonio
la source
9
Rappelez-vous que les listes ne sont pas implémentées en tant que listes à liaison unique en Python, cette opération est donc coûteuse (comme dans: la liste entière doit être copiée). Selon ce que vous souhaitez réaliser, cela peut ou non être un problème. Je le mentionne simplement parce que ce type de déstructuration de liste se trouve souvent dans les langages fonctionnels, où il s'agit en fait d'une opération très bon marché.
Niklas B.

Réponses:

189

Sous Python 3.x, vous pouvez le faire très 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]

Une nouvelle fonctionnalité de 3.x consiste à utiliser l' *opérateur lors du déballage, pour signifier toute valeur supplémentaire. Il est décrit dans PEP 3132 - Déballage itérable étendu . Cela a également l'avantage de travailler sur n'importe quel itérable, pas seulement des séquences.

C'est aussi très lisible.

Comme décrit dans le PEP, si vous voulez faire l'équivalent sous 2.x (sans potentiellement faire une liste temporaire), vous devez faire ceci:

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

Comme indiqué dans les commentaires, cela offre également la possibilité d'obtenir une valeur par défaut pour headplutôt que de lancer une exception. Si vous voulez ce comportement, next()prend un deuxième argument optionnel avec une valeur par défaut, donc next(it, None)vous donnerait Nones'il n'y avait pas d'élément head.

Naturellement, si vous travaillez sur une liste, le moyen le plus simple sans la syntaxe 3.x est:

head, tail = seq[0], seq[1:]
Gareth Latty
la source
1
désolé, j'ai mal utilisé le terme queue. Je veux dire ce que je dis dans l'exemple, c'est la liste sans le premier élément
Giacomo d'Antonio
1
@NikolayFominyh Ils sont tous les deux identiques - ils prennent tous les deux l'élément head et construisent une nouvelle liste contenant les éléments tail. Aucune différence de complexité. Une autre classe pourrait implémenter __getitem__/ __setitem__faire l'opération de queue paresseusement, mais pas la liste intégrée.
Gareth Latty
2
Sur une liste de 800 éléments le faisant 1M fois, j'ai 2,8s pour la tête, * tail = solution seq et seulement 1,8s pour la tête, tail = seq [0], seq [1:] solution. Le découpage est encore plus rapide pour les listes.
Cabu
2
@CMCDragonkai Non, la classe de liste principale de Python est une liste de tableaux. Ce serait O (n) car cela implique de copier la queue dans une nouvelle liste (avec un O (1) pour la tête).
Gareth Latty
1
Cette belle syntaxe est une autre raison de passer à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
la source
9

Pour la complexité d' head,tailopération O (1) , vous devez dequecependant utiliser .

Manière suivante:

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

C'est utile lorsque vous devez parcourir tous les éléments de la liste. Par exemple dans la fusion naïve de 2 partitions en tri par fusion.

Nikolay Fominyh
la source
Il semble que deque (list_instance) ait une complexité O (N). Ai-je tort?
Никита Конин
1
@ НикитаКонин, vous avez raison sur la construction de deque. Cependant, si vous voulez accéder au premier élément plus d'une fois, alors head, tail = l.popleft(), lest ~ O (1). head, tail = seq[0], seq[1:]est sur).
Nikolay Fominyh
Il semble que vous puissiez le faire head = l.popleft()et tailn'est qu'un alias pour l. Si les lchangements tailchangent aussi.
kon psych le
2

Python 2, utilisant 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]
BobIsNotMyName
la source
1
pourquoi diable feriez-vous cela au lieu de simplement head, tail = lst[0], lst[1:]? si OP signifie utiliser un littéral, alors il pourrait diviser la tête et la queue manuellementhead, tail = 1, [1, 2, 3, 5, 8, 13, 21, 34, 55]
Filipe Pina
1
(1) La question d'Op était de savoir s'il est possible de faire cela en une seule ligne (donc non lst = ...dans la ligne précédente). (2) Faire head, tail = lst[0], lst[1:]laisse le code ouvert aux effets secondaires (considérez head, tail = get_list()[0], get_list()[1:]), et est différent de la forme d'Op head, tail = **some_magic applied to** [1, 1, 2, 3, 5, 8, 13, 21, 34, 55].
BobIsNotMyName
Cela étant dit, je reconnais que c'est une mauvaise manière obscure d'obtenir la tête / la queue. Mais je pensais que c'était la meilleure réponse pour Python 2 à la question spécifique d'Op.
BobIsNotMyName
1

En s'appuyant sur la solution Python 2 de @GarethLatty , voici un moyen d'obtenir un équivalent sur une seule ligne sans variables intermédiaires dans 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 vous en avez besoin pour être à l'épreuve des exceptions (c'est-à-dire pour supporter une liste vide), ajoutez:

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

Si vous souhaitez le faire sans le point-virgule, utilisez:

h,t = ([(h,list(t)) for t in [iter([1,2,3,4])] for h in t]+[(None,[])])[0]
ABridgeTooFar
la source