web-dev-qa-db-fra.com

Comment définir la variable locale dans la compréhension de la liste?

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] 
25
Hao Tan

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]

50
Lying Dog

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))]
5
Xavier Guihot

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]
5
Vincent Goossens

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)))
4
behzad.nouri

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).

2
Ovaflo

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.

1

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?

0
Hao Tan