Python Language Expressions de générateur


Exemple

Les expressions de générateur sont très similaires aux compréhensions de liste. La principale différence est que cela ne crée pas un ensemble complet de résultats à la fois; il crée un objet générateur qui peut ensuite être itéré.

Par exemple, voyez la différence dans le code suivant:

# list comprehension
[x**2 for x in range(10)]
# Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Python 2.x 2.4
# generator comprehension
(x**2 for x in xrange(10))
# Output: <generator object <genexpr> at 0x11b4b7c80>

Ce sont deux objets très différents:

  • la compréhension de la liste renvoie un objet list alors que la compréhension du générateur renvoie un generator .

  • generator objets generator ne peuvent pas être indexés et utilisent la fonction next pour récupérer les éléments.

Note : Nous utilisons xrange car il crée aussi un objet générateur. Si nous utilisions la plage, une liste serait créée. De plus, xrange n'existe que dans la version ultérieure de python 2. Dans python 3, range ne fait que renvoyer un générateur. Pour plus d'informations, reportez-vous à l' exemple des fonctions Différences entre plage et xrange .


Python 2.x 2.4
g = (x**2 for x in xrange(10))
print(g[0])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'generator' object has no attribute '__getitem__'

g.next()  # 0
g.next()  # 1
g.next()  # 4
...
g.next()  # 81

g.next()  # Throws StopIteration Exception
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Python 3.x 3.0

REMARQUE: La fonction g.next() doit être remplacée par next(g) et xrange avec range car Iterator.next() et xrange() n'existent pas dans Python 3.


Bien que les deux puissent être répétés de la même manière:

for i in [x**2 for x in range(10)]:
    print(i)

"""
Out:
0
1
4
...
81
"""
Python 2.x 2.4
for i in (x**2 for x in xrange(10)):
    print(i)

"""
Out:
0
1
4
.
.
.
81
"""

Cas d'utilisation

Les expressions de générateur sont évaluées paresseusement, ce qui signifie qu'elles génèrent et renvoient chaque valeur uniquement lorsque le générateur est itéré. Ceci est souvent utile lorsque vous parcourez des jeux de données volumineux, évitant de créer un doublon du jeu de données en mémoire:

for square in (x**2 for x in range(1000000)):
    #do something

Un autre cas d'utilisation courant consiste à éviter de procéder à une itération complète si cela n'est pas nécessaire. Dans cet exemple, un élément est extrait d'une API distante à chaque itération de get_objects() . Des milliers d'objets peuvent exister, doivent être récupérés un par un et il suffit de savoir si un objet correspondant à un modèle existe. En utilisant une expression de générateur, lorsque nous rencontrons un objet correspondant au motif.

def get_objects():
    """Gets objects from an API one by one"""
    while True:
        yield get_next_item()

def object_matches_pattern(obj):
    # perform potentially complex calculation
    return matches_pattern

def right_item_exists():
    items = (object_matched_pattern(each) for each in get_objects())
    for item in items:
        if item.is_the_right_one:


            return True
    return False