English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Tutoriel de base Python

Contrôle de flux Python

Fonctions en Python

Types de données en Python

Opérations de fichiers Python

Objets et classes Python

Dates et heures Python

Connaissances avancées Python

Manuel de référence Python

Générateurs en Python

Dans cet article, vous apprendrez à créer des itérations faciles avec les générateurs Python, à ce que cela signifie par rapport aux itérateurs et aux fonctions normales, et pourquoi il est utile de les utiliser.

Qu'est-ce qu'un générateur en Python ?

Avec Pythonde constructionLes itérateursIl y a beaucoup de coûts; nous devons implémenter une classe en utilisant les méthodes __iter__() et __next__(), suivre l'état interne, déclencher StopIteration lorsque il n'y a pas de valeur à retourner, etc.

C'est à la fois long et contre-intuitif. Les générateurs peuvent être utiles dans ce cas.

Les générateurs Python sont une méthode simple pour créer des itérateurs. Tous les coûts mentionnés précédemment sont automatiquement traités par les générateurs Python.

En résumé, un générateur est une fonction qui retourne un objet (itérateur) que nous pouvons itérer (une valeur à la fois).

Comment créer un générateur en Python ?

Il est très simple de créer des générateurs en Python. Cela est aussi facile que de définir une fonction ordinaire en utilisant l'instruction yield plutôt que l'instruction return.

Si une fonction contient au moins une instruction yield (elle peut contenir d'autres instructions yield ou return), alors elle devient une fonction génératrice. yield et return retournent tous deux certaines valeurs de la fonction.

La différence réside en ce que lorsque la instruction return termine complètement une fonction, l'instruction yield met la fonction en pause et sauvegarde tous ses états, puis continue à s'exécuter lors des appels suivants.

La différence entre la fonction génératrice et la fonction normale

Voici la différence entre la fonction génératrice etLes fonctions normalesLes différences.

  • Les fonctions génératrices contiennent une ou plusieurs instructions yield.

  • Lors de l'appel, il retourne un objet (itérateur), mais ne commence pas immédiatement à s'exécuter.

  • Des méthodes telles que __iter__() et __next__() sont automatiquement implémentées. Par conséquent, nous pouvons utiliser next() pour parcourir les éléments.

  • Une fois que la fonction a produit un résultat, la fonction s'arrête et le contrôle est transféré à l'appelant.

  • Les variables locales et leur état sont mémorisées entre les appels successifs.

  • Finalement, lorsque la fonction se termine, StopIteration est automatiquement levée lors de la prochaine appel.

Ceci est un exemple pour illustrer tous les points mentionnés précédemment. Nous avons une fonction génératrice my_gen() nommée par plusieurs instructions yield.

# Une fonction de générateur simple
def my_gen():
    n = 1
    print('C'est la première impression')
    # La fonction de générateur contient une instruction yield
    yield n
    n += 1
    print('C'est la deuxième impression')
    yield n
    n += 1
    print('C'est la dernière impression')
    yield n

L'exécution interactive dans l'interpréteur est présentée comme suit. Exécutez ces commandes dans le Shell Python pour voir les sorties.

>>> # Il retourne un objet, mais ne commence pas immédiatement à s'exécuter.
>>> a = my_gen()
>>> # Nous pouvons utiliser next() pour parcourir ces éléments.
>>> next(a)
C'est la première impression
1
>>> # Une fois que la fonction a produit un résultat, la fonction s'arrête et le contrôle est transféré à l'appelant.
>>> # Les variables locales et leur état sont mémorisés entre les appels successifs.
>>> next(a)
C'est la deuxième impression
2
>>> next(a)
C'est la dernière impression
3
>>> # Lorsque la fonction se termine, StopIteration est automatiquement déclenché lors de l'appel suivant.
>>> next(a)
Traceback (appel le plus récent en dernier) :
...
StopIteration
>>> next(a)
Traceback (appel le plus récent en dernier) :
...
StopIteration

Une chose amusante à noter dans l'exemple ci-dessus est que les variables sont mémorisées entre les appels.ndes valeurs.

Différent des fonctions normales, les variables locales ne sont pas détruites lors de la création de la fonction. De plus, l'objet de générateur ne peut être itéré qu'une seule fois.

Pour redémarrer ce processus, nous devons utiliser = my_gen() pour créer un autre objet de générateur.

Attention :Le dernier point à noter est que nous pouvons directement utiliser le générateur avecBoucle forUtilisés ensemble.

Cela est dû au fait que la boucle for accepte un itérateur et l'itére avec la fonction next(). Lorsque StopIteration est déclenché, il se termine automatiquement.Comprendre comment implémenter réellement la boucle for en Python.

# Une fonction de générateur simple
def my_gen():
    n = 1
    print('C'est la première impression')
    # La fonction de générateur contient une instruction yield
    yield n
    n += 1
    print('C'est la deuxième impression')
    yield n
    n += 1
    print('C'est la dernière impression')
    yield n
# Utilisation de la boucle for
for item in my_gen():
    print(item)

Lorsque le programme est exécuté, la sortie est :

C'est la première impression
1
C'est la deuxième impression
2
C'est la dernière impression
3

Générateur Python avec boucle

L'exemple ci-dessus n'est pas très utile, nous l'étudions juste pour comprendre ce qui se passe en arrière-plan.

Généralement, les fonctions de générateur sont implémentées par un cycle avec une condition de terminaison appropriée.

Laissez-nous prendre un exemple de générateur pour inverser une chaîne de caractères.

def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1,-1,-1) :
        yield my_str[i]
# La boucle for inverse une chaîne de caractères
# Sortie:
# o
# l
# l
# e
# h
for char in rev_str("hello"):
     print(char)

Dans cet exemple, nous utilisons la fonction range() pour obtenir les indices en ordre inverse avec une boucle for.

Il s'avère que cette fonction générateur s'applique non seulement aux chaînes de caractères, mais aussi à d'autres types d'objets itérables, tels quelist,tupleetc.

Expression générateur Python

L'utilisation des expressions générateur permet de créer facilement des générateurs simples de manière dynamique. Cela rend la construction de générateurs facile.

créée par lambdaidentique à une fonction anonymecrée une fonction anonyme générateur.

La syntaxe des expressions générateur est similaire àPythonenCompréhensionLa syntaxe. Mais en remplaçant les crochets par des parenthèses.

La principale différence entre la compréhension de liste et l'expression générateur réside en ce que, bien que la compréhension de liste génère la liste entière, l'expression générateur génère un élément à la fois.

Ils sont un peu paresseux, ils génèrent des éléments uniquement lorsque cela est nécessaire. Pour cette raison, les expressions générateur sont beaucoup plus efficaces en termes de mémoire par rapport à une compréhension de liste équivalente.

# Initialisation de la liste
my_list = [1, 3, 6, 10]
# Utilisation de la compréhension de liste pour multiplier chaque élément
# Sortie: [1, 9, 36, 100]
[x**2 for x in my_list]
# La même chose peut être réalisée avec une expression générateur
# Sortie: <generator object <genexpr> at 0x0000000002EBDAF8>
(x**2 for x in my_list)

Nous pouvons voir ci-dessus que l'expression générateur ne produit pas immédiatement le résultat souhaité. Au lieu de cela, elle retourne un objet générateur qui produit des éléments à la demande.

# Initialisation de la liste
my_list = [1, 3, 6, 10]
a = (x**2 for x in my_list)
# Sortie: 1
print(next(a))
# Sortie: 9
print(next(a))
# Sortie: 36
print(next(a))
# Sortie: 100
print(next(a))
# Sortie: StopIteration
next(a)

Les expressions générateurs peuvent être utilisées à l'intérieur des fonctions. Lorsqu'elles sont utilisées de cette manière, les parenthèses peuvent être supprimées.

>>> sum(x**2 for x in my_list)
146
>>> max(x**2 for x in my_list)
100

Pourquoi utiliser des générateurs en Python ?

Il y a plusieurs raisons qui rendent les générateurs une implémentation attrayante.

1.Facile à mettre en œuvre

Les générateurs peuvent être implémentés de manière claire et concise par rapport aux éléments correspondants de la classe itératrice. Voici un exemple d'implémentation en utilisant la classe itératrice2d'exemple de séquence de puissances.

class PowTwo:
    def __init__(self, max = 0):
        self.max = max
    def __iter__(self):
        self.n = 0
        return self
    def __next__(self):
        if self.n > self.max:
            raise StopIteration
        result = 2 ** self.n
        self.n += 1
        return result

This code is long. Now, perform the same operation using the generator function.

def PowTwoGen(max = 0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1

Since generators automatically track details, they are concise and clear, and the implementation is also more concise.

2. Saves memory

A regular function that returns a sequence creates the entire sequence in memory before returning the result. If the number of items in the sequence is large, it can affect efficiency.

And the generator implementation of this sequence is memory-friendly, so it is the preferred choice because it can only generate one item at a time.

3. Represents an infinite stream

Generators are an excellent medium for representing infinite data streams. Infinite streams cannot be stored in memory, and since generators generate one item at a time, they can represent infinite data streams.

The following example can generate all even numbers (at least in theory).

def all_even():
    n = 0
    while True:
        yield n
        n += 2

4. Pipeline generator

Generators can be used for pipelining a series of operations. It is best to illustrate with an example.

Assume we have a log file of a famous fast-food chain. The log file has a column (the4Column), which tracks the number of pizzas sold per hour, we want to sum it up to get5Total number of pizzas sold in the year.

Assume all content is strings, and no available numbers are marked as "N / A. Implementation of generators can be as follows.

with open('sells.log') as file:
    pizza_col = (line[3] for line in file)
    per_hour = (int(x) for x in pizza_col if x != 'N/A')
    print("Total pizzas sold = ", sum(per_hour))

This pipeline is efficient and easy to read (yes, very cool!).