Comment filtrer efficacement les valeurs calculées dans une compréhension de liste Python?

StackOverflow https://stackoverflow.com/questions/130262

  •  02-07-2019
  •  | 
  •  

Question

La syntaxe de compréhension de liste Python facilite le filtrage des valeurs dans une compréhension. Par exemple:

result = [x**2 for x in mylist if type(x) is int]

Renverra une liste des carrés d'entiers dans mylist. Cependant, que se passe-t-il si le test implique des calculs (coûteux) et que vous souhaitez filtrer le résultat? Une option est:

result = [expensive(x) for x in mylist if expensive(x)]

Cela entraînera une liste de messages "" false". cher (x), bien que cher () est appelé deux fois pour chaque x. Existe-t-il une syntaxe de compréhension vous permettant de faire ce test en appelant seulement une fois par x cher?

Était-ce utile?

La solution

Si les calculs sont déjà bien intégrés dans les fonctions, pourquoi ne pas utiliser filtre et map ?

result = filter (None, map (expensive, mylist))

Vous pouvez utiliser itertools.imap si la liste est très longue.

Autres conseils

Je suis venu avec ma propre réponse après une minute de réflexion. Cela peut être fait avec des compréhensions imbriquées:

result = [y for y in (expensive(x) for x in mylist) if y]

Je suppose que cela fonctionne, même si je trouve que les compréhensions imbriquées ne sont lisibles que marginalement

La réponse la plus évidente (et la plus lisible, selon moi) est de ne pas utiliser une compréhension de liste ou une expression de générateur, mais plutôt un véritable générateur:

def gen_expensive(mylist):
    for item in mylist:
        result = expensive(item)
        if result:
            yield result

Cela prend plus d’espace horizontal, mais il est beaucoup plus facile de voir ce qu’il fait en un coup d’œil, et vous finissez par ne pas vous répéter.

result = [x for x in map(expensive,mylist) if x]

map () retournera une liste des valeurs de chaque objet de la liste mylist passée à Cher (). Ensuite, vous pouvez comprendre cette liste et ignorer les valeurs inutiles.

Cela ressemble un peu à une compréhension imbriquée, mais devrait être plus rapide (puisque l’interpréteur python peut l’optimiser assez facilement).

C’est exactement ce que les générateurs sont adaptés à gérer:

result = (expensive(x) for x in mylist)
result = (do_something(x) for x in result if some_condition(x))
...
result = [x for x in result if x]  # finally, a list
  1. Cela rend parfaitement clair ce qui se passe à chaque étape du pipeline.
  2. Explicite sur implicite
  3. Utilise des générateurs partout jusqu'à la dernière étape, donc pas de grandes listes intermédiaires

cf: "Astuces du générateur pour les programmeurs système" par David Beazley

Vous pouvez toujours mémoriser la fonction onéreuse () afin que le fait d'appeler la deuxième fois n'est qu'une recherche de la valeur calculée de x .

Voici une des nombreuses implémentations de la mémoire en tant que décorateur . / p>

Vous pouvez mémoriser cher (x) (et si vous appelez souvent (x) fréquemment, vous devriez probablement le mémoriser de quelque façon que ce soit. Cette page donne une implémentation de memoize pour python:

http://code.activestate.com/recipes/52201/

Cela présente l’avantage supplémentaire que les opérations onéreuses (x) peuvent être exécutées moins que N fois, car toute entrée dupliquée utilisera le mémo de l’exécution précédente.

Notez que cela suppose que cher (x) est une vraie fonction et ne dépend pas d'un état externe susceptible de changer. Si cher (x) dépend d'un état externe et que vous pouvez détecter le moment où cet état change, ou si vous savez qu'il ne changera pas pendant la compréhension de votre liste, vous pouvez réinitialiser les mémos avant la compréhension.

J'aurai une préférence pour:

itertools.ifilter(bool, (expensive(x) for x in mylist))

Cela présente l'avantage de:

Il y a l'utilisation ancienne et simple d'une boucle pour à ajouter à une liste:

result = []
for x in mylist:
    expense = expensive(x)
    if expense:
        result.append(expense)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top