J'ai une méthode qui prend une liste et retourne un objet:
# input a list, returns an object
def map_to_obj(lst):
a_list = f(lst)
return a_list[0] if a_list else None
Je veux obtenir une liste qui contient tous les éléments mappés qui ne sont pas None
.
Comme ça:
v_list = [v1, v2, v3, v4]
[map_to_obj(v) for v in v_list if map_to_obj(v)]
Mais il ne semble pas bon d'appeler le map_to_obj
méthode deux fois dans la liste de compréhension.
Existe-t-il un moyen d'avoir des variables locales dans les compréhensions de liste afin qu'elles puissent avoir de meilleures performances?
Ou le compilateur l'optimise-t-il automatiquement?
Voici ce que je veux:
(sml like)
[let mapped = map_to_obj(v) in for v in v_list if mapped end]
Utilisez la compréhension des listes imbriquées:
[x for x in [map_to_obj(v) for v in v_list] if x]
ou mieux encore, une compréhension de la liste autour d'une expression de générateur:
[x for x in (map_to_obj(v) for v in v_list) if x]
A partir de Python 3.8
, Et l'introduction de expressions d'affectation (PEP 572) (opérateur :=
), Il est possible d'utiliser une variable locale dans une liste de compréhension afin d'éviter appeler deux fois la même fonction:
Dans notre cas, nous pouvons nommer l'évaluation de map_to_obj(v)
en tant que variable o
tout en utilisant le résultat de l'expression pour filtrer la liste; et donc utiliser o
comme valeur mappée:
[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]
Une affectation de variable n'est qu'une liaison singulière:
[x for v in l for x in [v]]
Il s'agit d'une réponse plus générale et également plus proche de ce que vous proposiez. Donc, pour votre problème, vous pouvez écrire:
[x for v in v_list for x in [map_to_obj(v)] if x]
Vous pouvez éviter le recalcul en utilisant python intégré filter
:
list(filter(lambda t: t is not None, map(map_to_obj, v_list)))
Une variable locale peut être définie dans une compréhension en trichant un peu et en utilisant un "pour" supplémentaire qui "itère" à travers un Tuple à 1 élément contenant la valeur souhaitée pour la variable locale. Voici une solution au problème du PO en utilisant cette approche:
[o for v in v_list for o in (map_to_obj(v),) if o]
Ici, o
est la variable locale étant définie égale à map_to_obj(v)
pour chaque v
.
Dans mes tests, cela est légèrement plus rapide que l'expression de générateur imbriqué de Lying Dog (et également plus rapide que le double appel de l'OP à map_to_obj(v)
, qui, étonnamment, peut être plus rapide que l'expression de générateur imbriqué si le map_to_obj
n'est pas trop lente).
Les compréhensions de liste sont très bien pour les cas simples, mais parfois une ancienne boucle for
est la solution la plus simple:
other_list = []
for v in v_list:
obj = map_to_obj(v)
if obj:
other_list.append(obj)
Maintenant, si vous voulez vraiment une liste comp et que vous ne voulez pas construire une liste tmp, vous pouvez utiliser les versions de l'itérateur de filter
et map
:
import itertools as it
result = list(it.ifilter(None, it.imap(map_to_obj, v_list)))
ou plus simplement:
import itertools as it
result = filter(None, it.imap(map_to_obj, v_list)))
Les versions de l'itérateur ne construisent pas de liste temporaire, elles utilisent une évaluation paresseuse.
J'ai trouvé un moyen d'utiliser reduce
:
def map_and_append(lst, v):
mapped = map_to_obj(v)
if mapped is not None:
lst.append(mapped)
return lst
reduce(map_and_append, v_list, [])
Que diriez-vous de l'exécution de ceci?