web-dev-qa-db-fra.com

compréhension de la liste de pause

Comment puis-je briser une compréhension de liste basée sur une condition, par exemple lorsque le nombre 412 est trouvé?

Code:

numbers = [951, 402, 984, 651, 360, 69, 408, 319, 601, 485, 980, 507, 725, 547, 544,
           615, 83, 165, 141, 501, 263, 617, 865, 575, 219, 390, 984, 592, 236, 105, 942, 941,
           386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399,
           162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67,
           104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958, 609, 842,
           451, 688, 753, 854, 685, 93, 857, 440, 380, 126, 721, 328, 753, 470, 743, 527]

even = [n for n in numbers if 0 == n % 2]

Donc, fonctionnellement, ce serait quelque chose que vous pouvez déduire que cela est censé faire:

even = [n for n in numbers if 0 == n % 2 and break if n == 412]

Je vraiment préfère:

  • une doublure
  • pas d'autres bibliothèques fantaisistes comme itertools, "pure python" si possible (lire: la solution ne doit utiliser aucune instruction import ou similaire)
49
Flavius
even = [n for n in numbers[:None if 412 not in numbers else numbers.index(412)] if not n % 2] 

Je viens de prendre le code de F.J. ci-dessus et d'ajouter un ternaire pour vérifier si 412 est dans la liste. Toujours un "liner" et fonctionnera même si le 412 n'est pas dans la liste.

14

Utilisez une fonction pour augmenter StopIteration et list pour l'attraper:

>>> def end_of_loop():
...     raise StopIteration
... 
>>> even = list(end_of_loop() if n == 412 else n for n in numbers if 0 == n % 2)
>>> print(even)
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418, 344, 236, 566, 978, 328, 162, 758, 918]

Pour ceux qui se plaignent, ce n'est pas un vol simple:

even = list(next(iter(())) if n == 412 else n for n in numbers if 0 == n % 2)

Pour ceux qui se plaignent, c'est hackish et ne devrait pas être utilisé dans le code de production: Eh bien, vous avez raison. Certainement.

53
WolframH

Vous pouvez utiliser des expressions de générateur avec itertools.takewhile():

even_numbers = (n for n in numbers if not n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

Edit: Je viens de remarquer l'exigence de ne pas utiliser de imports. Eh bien, je laisse cette réponse ici de toute façon.

43
Sven Marnach

Si 412 sera définitivement dans la liste, vous pouvez utiliser ceci:

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

Si vous souhaitez inclure 412 dans le résultat, utilisez simplement numbers[:numbers.index(412)+1] pour la tranche.

Notez qu'en raison de la tranche, cela sera moins efficace (au moins en termes de mémoire) qu'un itertools ou une solution de boucle.

14
Andrew Clark

Je sais que c'est un très vieux message, mais depuis qu'OP m'a demandé d'utiliser break dans un list-comprehension Et que je cherchais également quelque chose de similaire, j'ai pensé publier mes résultats ici pour référence future.

En enquêtant sur break, je suis tombé sur une fonction peu connue de iter sous la forme iter(callable, sentinel) qui renvoie un itérateur qui "casse" itération une fois appelable function value est égal à sentinel value.

>>> help(iter)
Help on built-in function iter in module __builtin__:

iter(...)
    iter(collection) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.

La partie délicate ici consiste à définir une fonction qui conviendrait à un problème donné. Dans ce cas, nous devons d'abord convertir un list de numbers en iterator en utilisant x = iter(numbers) qui se nourrit en tant que variable externe en lambda une fonction.

Ensuite, notre fonction appelable est juste un appel à l'itérateur pour cracher la valeur suivante. L'itérateur compare ensuite avec notre valeur sentinelle (412 dans ce cas) et "casse" une fois cette valeur atteinte.

print [i for i in iter(lambda x=iter(numbers): next(x),412) if i %2 == 0]

>>> 
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418,  
 344, 236, 566, 978, 328, 162, 758, 918]
14
Anil_M

La syntaxe pour les affichages de liste (y compris les compréhensions de liste) est ici: http://docs.python.org/reference/expressions.html#list-displays

Comme vous pouvez le voir, il n'y a pas de syntaxe spéciale while ou until. Le plus proche que vous pouvez obtenir est:

even_numbers = (n for n in numbers if 0 == n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

(Code extrait de la réponse de Sven Marnach, affiché pendant que je tapais ceci).

4
Marcin

une autre solution en ligne sournoise pour résoudre breaking in list comprehension, à l'aide de la condition end.

sans utiliser numbers.index(412), peut-être un peu plus vite?

even = [n for end in [[]] for n in numbers
        if (False if end or n != 412 else end.append(42))
        or not end and not n % 2]

Remarque: c'est une mauvaise idée. juste pour le fun : )

comme l'a dit @WolframH:

Pour ceux qui se plaignent, c'est hackish et ne devrait pas être utilisé dans le code de production: Eh bien, vous avez raison. Certainement.

0
recnac