Python liste compréhensions pour créer plusieurs listes
Je veux créer deux listes listOfA
et listOfB
pour stocker les indices de A
et B
à partir d'une autre liste s.
s=['A','B','A','A','A','B','B']
De sortie devrait être de deux listes
listOfA=[0,2,3,4]
listOfB=[1,5,6]
Je suis capable de le faire avec deux états.
listOfA=[idx for idx,x in enumerate(s) if x=='A']
listOfB=[idx for idx,x in enumerate(s) if x=='B']
Cependant, je tiens à le faire en une seule itération en utilisant la liste des compréhensions.
Est-il possible de le faire en une seule instruction?
quelque chose comme listOfA,listOfB=[--code goes here--]
source d'informationauteur Heisenberg
Vous devez vous connecter pour publier un commentaire.
La définition même d'une compréhension de liste est de produire un des objets de la liste. Votre 2 les objets de la liste sont de longueurs différentes même; vous auriez à utiliser des effets secondaires à réaliser ce que vous voulez.
N'utilisez pas les interprétations de la liste ici. Viens d'utiliser un simple boucle:
Cela vous laisse juste un boucle à exécuter; cela va battre tout les deux interprétations de la liste, au moins pas jusqu'à ce que les développeurs de trouver un moyen de faire des interprétations de la liste de générer une liste de deux fois plus rapide qu'une boucle avec distinct
list.append()
appels.Je choisirais ce toute la journée sur une liste imbriquée compréhension juste pour être en mesure de produire deux listes sur une seule ligne. Comme le Zen de Python états:
Sorte de; la clé est de générer un 2-liste d'éléments que vous pouvez ensuite décompresser:
Cela dit, je pense que c'est assez stupide pour le faire de cette façon, explicite boucle est beaucoup plus lisible.
Une belle approche de ce problème est l'utilisation defaultdict. @Martin déjà dit, la compréhension de liste n'est pas le bon outil pour produire des deux listes. À l'aide de defaultdict vous permettrait de créer une ségrégation à l'aide d'une seule itération. De plus, votre code ne serait pas limitée à une forme quelconque.
Ce que vous essayez de réaliser n'est pas impossible, c'est juste compliqué, et probablement inutile.
Si vous voulez partitionner un objet iterable en deux iterables, si la source est une liste ou d'une autre ré-utilisable itératif, vous êtes probablement mieux de le faire en deux passes, comme dans votre question.
Même si la source est un itérateur, si la sortie que vous voulez, c'est une paire de listes, pas une paire de paresseux itérateurs, soit utiliser Martijn réponseou faire deux passes de plus de
list(iterator)
.)Mais si vous avez vraiment besoin paresseusement à la partition arbitraire itérable en deux iterables, il n'y a pas moyen de le faire sans une sorte de stockage intermédiaire.
Disons que vous partition
[1, 2, -1, 3, 4, -2]
enpositives
etnegatives
. Maintenant à vous d'essayer denext(negatives)
. Cela devrait vous donner-1
droit? Mais il ne peut pas le faire sans consommer de la1
et la2
. Ce qui signifie que lorsque vous essayez denext(positives)
vous allez obtenir3
au lieu de1
. Ainsi, la1
et2
besoin pour obtenir les stocker quelque part.Plus de l'intelligence que vous avez besoin est enveloppé à l'intérieur de
itertools.té
. Si vous venez de fairepositives
etnegatives
en deux teed copies de la même itération, puis les filtrer à la fois, vous avez terminé.En fait, c'est l'une des recettes dans la
itertools
docs:(Si vous ne pouvez pas comprendre cela, c'est probablement la peine de l'écrire explicitement, avec deux générateur de fonctions de partage d'un itérateur, et un tee-shirt par une fermeture, ou les deux méthodes d'une classe de les partager via
self
. Il convient de quelques dizaines de lignes de code qui n'a pas besoin de quelque chose de délicat.)Et vous pouvez même obtenir
partition
comme une importation à partir d'une bibliothèque tierce, commemore_itertools
.Maintenant, vous pouvez l'utiliser dans un one-liner:
... et vous avez un itérateur sur toutes les valeurs positives, et un itérateur sur toutes les valeurs négatives. On dirait qu'ils sont complètement indépendants, mais ensemble, ils ne font qu'un seul passage sur
lst
—de sorte qu'il fonctionne même si vous attribuezlst
à un générateur d'expression ou d'un fichier ou quelque chose au lieu d'une liste.Alors, pourquoi n'est-ce pas là une sorte de raccourci de la syntaxe pour cela? Parce qu'il serait assez trompeuse.
Une compréhension ne prend pas d'espace de stockage supplémentaire. C'est la raison pour générateur d'expressions sont—elles peuvent transformer un paresseux itérateur dans un autre paresseux itérateur sans stocker quoi que ce soit.
Mais cela prend
O(N)
de stockage. Imaginez tous les nombres sont positifs, mais vous essayez d'effectuer une itérationnegative
premier. Ce qui se passe? Tous les numéros obtenir poussé àtrueq
. En fait, queO(N)
pourrait même être infini (par exemple, l'essayer suritertools.count()
).C'est bien pour quelque chose comme
itertools.tee
une fonction coincé dans un module que la plupart des novices ne même pas connaître, et qui a de belles docs qui peuvent expliquer ce qu'il fait et de faire les frais de claire. Mais le faire avec du sucre syntaxique qui l'a fait ressembler à une compréhension de la normale serait une autre histoire.