Question Une seule ligne pour itérateur sur boucle avec un filtre "if"?


Question bête:
J'ai une boucle simple suivie d'une instruction if simple:

for airport in airports:
    if airport.is_important:

et je me demandais si je pouvais écrire ceci en une seule ligne.
Donc, oui, je peux le faire:

for airport in (airport for airport in airports if airport.is_important):

mais il se lit si ridicule et redondant (for airport in airport for airport in airports...).
Y a-t-il une meilleure façon?


46
2018-03-08 14:03


origine


Réponses:


Non, il n'y a pas de moyen plus court. Habituellement, vous pourrez même le diviser en deux lignes:

important_airports = (airport for airport in airports if airport.is_important)
for airport in important_airports:
    # do stuff

Ceci est plus flexible, plus facile à lire et ne consomme toujours pas beaucoup de mémoire.


63
2018-03-08 14:14



Vous pourriez faire

for airport in filter(lamdba x: x.is_important, airports):
    # do stuff...

24
2018-03-08 14:08



J'utiliserais une garde négative sur la boucle. Il est lisible et n'introduit pas d'indentation supplémentaire.

for airport in airports:
    if not airport.is_important: continue
    <body of loop>

14
2018-03-08 20:16



Mabe ça, mais c'est plus ou moins la même chose verbeuse ...

import itertools

for airport in itertools.ifilter(lambda x: x.is_important, airports):
    ...

5
2018-03-08 14:08



Ceci est une philosophie de conception de python. Si cela vous prend trop de mots pour le mettre sur une ligne, il devrait être divisé en quelques lignes pour aider la personne qui vient après vous. Les expressions de liste et de générateur sont plus utiles pour transformer les itérables en place - ce qui rend les formes plus lisibles de map et filter.


1
2018-03-08 14:12



Voici une alternative à certaines autres versions de filtre:

from operator import attrgetter as attr
for airport in filter(attr('is_important'), airports):
    ...

Cela présente l'avantage d'être assez concis et de vous permettre d'utiliser la notation dot attr ('first_class.is_full').

Vous pourriez également mettre quelque chose comme ça (ou une version utilisant une compréhension de liste) dans une fonction utilitaire comme filter_by_attr. Ensuite, vous pouvez faire:

for airport in filter_by_attr(airports, 'is_important'):
    ...

Je pense toujours que e-satis a raison de le mettre dans une nouvelle variable, peu importe la méthode que vous utilisez. C'est plus clair, surtout si l'utilisation ne correspond pas exactement au nom de l'attribut en question (ou si le critère est plus complexe).

Ma seule remarque à ce sujet serait que si vous vous retrouvez à utiliser cela à plusieurs endroits, vous devriez peut-être faire des aéroports une collection spéciale avec «important_airports» étant un @property qui renvoie la collection filtrée. Ou une autre sorte d'abstraction pour masquer le filtrage (comme un appel de service).


1
2017-09-11 00:13



Utiliser la compréhension de liste (seulement si les aéroports sont une liste d'objets):

for airport in [a for a in airports if a.is_important]:

0
2018-03-08 14:11