J'ai essayé d'expliquer à quoi sert le générateur Python aussi facilement que possible.

introduction

Il y a d'autres pages qui expliquent les générateurs Python, mais il y en avait beaucoup qui étaient en anglais ou qui ne me semblaient pas bien, alors j'ai expliqué les générateurs à ma manière, également pour organiser mes pensées. Je vais essayer. Si je refuse, je suis fondamentalement confus, alors je pourrais dire quelque chose de mal. Si vous avez des erreurs, veuillez commenter et cela vous aidera grandement à étudier. De plus, l'utilisation du générateur présenté ici n'est que quelques exemples d'utilisation, et cela ne signifie pas qu'il n'y a pas d'autre utilisation.

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

Il existe d'autres excellents articles pour des explications détaillées et précises, veuillez donc vous y référer: Itérateur et générateur Python

Pour le dire brièvement, pensez-y comme "une fonction normale avec l'instruction return remplacée par yield". (C'est différent, mais veuillez le manquer pour le moment). Si vous écrivez yield au lieu de return, ** l'appelant ne renverra pas de valeur **. Au lieu de cela, il sera appelé avec une instruction for, etc. et ** retournera les valeurs dans l'ordre **. C'est probablement plus facile à comprendre en donnant un exemple.

example.py


def count_up():
    x = 0
    while True:
        yield x
        x += 1

Certes, c'est le rendement, pas le retour. Et quelque chose se passe après * yield *. Puisqu'il n'y a pas de traitement après l'instruction return, vous pouvez immédiatement voir que c'est quelque chose de différent du simple remplacement du retour d'une fonction normale. Je vais vous l'expliquer beaucoup. Pour le moment, ce générateur appelle par exemple:

>>> countup()
<generator object countup at 0x101fa0468>
>>> for i in countup():
...     print(i)
...     if i == 5:
...             break
0
1
2
3
4
5

Certes, il semble qu'aucune valeur ne soit renvoyée simplement en appelant. Au lieu de cela, lorsqu'elles sont appelées dans une instruction for, les valeurs sont renvoyées dans l'ordre. Ce qui se passe à ce moment, c'est que count_up renvoie la valeur de x à chaque fois qu'il est appelé, mais il incrémente la valeur de x à chaque fois qu'il est appelé. C'est ce que le processus vient après le rendement. Maintenant que vous savez ce qu'est le générateur (ne vous inquiétez pas, je vais vous donner quelques exemples si vous n'êtes pas sûr). Mais ce à quoi vous pensez probablement ici, c'est ** Où utilisez-vous cela? ** ** Je pense que c'est ce que cela signifie. Donc, cette fois, j'expliquerai le but du générateur à ma manière.

Différence entre générateur et fonction

Comme vous pouvez le voir dans l'exemple précédent, le générateur et la fonction sont ** complètement différents **. Les générateurs sont extrêmement plus proches des ** classes ** que des fonctions. Par exemple, dans l'exemple précédent

>>> countup()

Peut être clairement compris en interprétant qu'il créait une instance de la classe. Ainsi, le code suivant fonctionne également:

>>> y = countup()
>>> for i in y:
...     print(i)
...     if i == 5:
...             break
0
1
2
3
4
5

À titre de test, si vous exécutez le processus suivant après cela,

>>> for i in y:
...     print(i)
...     if i == 10:
...             break
6
7
8
9
10

0 à 5 ont disparu! La raison en est que le générateur ** se souvient de ce qui a été fait ** à chaque fois qu'il a été appelé. En d'autres termes, il a un ** état **. C'est la plus grande différence avec la fonction. Dans l'exemple ci-dessus, x = 6 en y lorsque la première instruction for se termine, et cela continue jusqu'à la prochaine instruction for.

Donc pourquoi est-ce important? À propos, considérons, par exemple, la scène du calcul de la séquence de nombres de Fibonacci que tout le monde aime. Une méthode de calcul couramment introduite utilisant la récurrence est la suivante:

fibonacci_recursive.py


def fibonacci(n):
    if n == 0:
        return 0
    if n == 1:
        return 1
    return fibonacci(n-1) + fibonacci(n-2)

Ce serait bien si n était petit, mais si n était grand, la pile se remplirait et une erreur serait générée. Par contre, lors de l'utilisation d'un générateur

fibonacci_generator.py


def fibonacci():
    a, b = 0, 1
    while 1:
        yield b
        a, b = b, a+b

De cette façon, en conservant les valeurs des deux dernières chaînes comme états, il devient possible de calculer la séquence de Fibonacci sans gaspiller la pile.

Différence entre générateur et liste

En regardant les exemples jusqu'à présent, le lecteur pense probablement ** La liste n'est-elle pas bonne? ** ** Je pense que c'est ce que cela signifie. Si vous souhaitez simplement imprimer à partir de zéro, vous pouvez certainement utiliser une liste distincte. Ensuite, quand ce n'est pas bon pour une liste, c'est ** quand il est difficile de garder la liste entière en mémoire et c'est inutile **. Par exemple, considérons le même exemple de calcul du Nième élément de la séquence de Fibonacci à l'aide d'une liste:

fibonacci_list.py


f = [0, 1]
n = 2
while n < N:
    f.append(f[n-1] + f[n-2])
    n += 1

Si vous voulez juste obtenir la Nième valeur, les valeurs de la séquence de Fibonacci avant la N-3e sont une perte de mémoire. En d'autres termes, la scène dans laquelle le générateur fonctionne est ** la scène dans laquelle vous devez renvoyer des valeurs en séquence et avoir un état, mais vous n'avez pas besoin de conserver la liste entière **. Je voudrais que vous imaginiez un automate à états finis avec un symbole de sortie. Au contraire, il ne convient pas aux situations où vous n'avez pas besoin d'avoir un état séparé ou vous devez tenir une liste. Le premier exemple sert à calculer une fonction de hachage pour un nombre d'entrée, et le dernier exemple à créer une liste de nombres premiers. Ci-dessous, nous examinerons quelques exemples plus pratiques.

Utilisation pratique du générateur

Analyse de phrase

Un exemple où le générateur est réellement utilisé est la bibliothèque standard pour l'analyse des phrases python: http://docs.python.jp/2/library/tokenize.html L'analyse des phrases est un processus souvent effectué lors de la compilation d'un programme. Elle examine le programme caractère par caractère et divise la chaîne de caractères du programme en parties importantes (appelées jetons). Par exemple def f(hoge, foo): Je veux dire, def, f, (, hoge, foo, ), : Il sera probablement divisé en une chaîne de jetons appelée. Vous pouvez dire à partir de la chaîne def qu'il s'agit d'une définition de fonction, f est le nom de la fonction et les variables séparées par des virgules entre les "(" et ")" après lui sont les arguments. L'analyse de phrase ajoute également ces informations au jeton et transmet la chaîne de jeton au processus suivant (ce qui peut être un peu inexact, mais consultez la documentation du compilateur pour plus d'informations).

Ce qui est important ici, c'est que lorsque vous regardez chaque caractère et que vous l'analysez, par exemple, le traitement lorsque vous voyez le caractère ")" change selon que vous voyez "(" ou non. .. Autrement dit, il doit avoir un ** état **. Mais une fois cette fonction définie, ** les informations sur cette fonction n'ont pas d'importance **. Ce serait une perte de mémoire pour enregistrer les informations de l'ensemble du programme une par une. Par conséquent, l'analyse des phrases est une bonne scène pour que les générateurs jouent un rôle actif.

pipeline

Les détails sont expliqués sur le site suivant. https://brett.is/writing/about/generator-pipelines-in-python/

En termes simples, un pipeline de générateur est un processus de ** connexion ** de plusieurs générateurs. J'espère que vous pouvez imaginer le traitement de type convoyeur à bande du côté de l'usine. L'exemple présenté est un pipeline qui triple les éléments pairs d'une liste donnée d'entiers, les convertit en chaîne et les renvoie.

pipeline.py


def even_filter(nums):
    for num in nums:
        if num % 2 == 0:
            yield num
def multiply_by_three(nums):
    for num in nums:
        yield num * 3
def convert_to_string(nums):
    for num in nums:
        yield 'The Number: %s' % num

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pipeline = convert_to_string(multiply_by_three(even_filter(nums)))
for num in pipeline:
    print num

Dans cet exemple, les générateurs individuels n'ont pas d'état, mais ils utilisent toujours les générateurs efficacement. C'est une situation où vous n'avez pas à enregistrer la liste entière, mais vous devez la traiter séquentiellement. Vous pouvez penser que le traitement des fonctions est correct, mais le traitement des valeurs impaires, par exemple, est juste un calcul inutile dans l'exemple ci-dessus. C'est pourquoi un générateur qui peut appliquer un traitement à chaque élément un par un est pratique.

Récursif

Les sites suivants ont des explications détaillées et des exemples: http://www.unixuser.org/~euske/doc/python/recursion-j.html

Comme je l'ai expliqué plus tôt, le gros avantage des générateurs est qu'ils peuvent traiter de manière séquentielle sans conserver la liste entière. En d'autres termes, il est compatible avec des problèmes qui peuvent être subdivisés et résolus par un traitement itératif. Vous pouvez voir que cela est très proche de l'idée de résoudre les problèmes de récurrence. L'exemple de séquence de Fibonacci était un exemple de remplacement récursif par un générateur, mais bien sûr, vous pouvez également utiliser le générateur lui-même de manière récursive. L'avantage d'utiliser un générateur au lieu d'une fonction est qu'une fois que vous avez généré une valeur, vous pouvez la rejeter à la volée. Par exemple, le code qui utilise le générateur de manière récursive pour parcourir l'arborescence ressemble à ceci:

tree.py


class Node:
    def __init__(self, data):
        self.data = data
	self.left = None
	self.right = None
		

def traverse(node):
    if node is not None:
	    for x in traverse(node.left):
	        yield x
        yield t.dat
	    for x in traverse(node.right):
	        yield x

Au fait, si vous utilisez l'instruction yield from nouvellement ajoutée à partir de la série python3.3, la fonction traverse sera encore plus propre.

def traverse(node):
    if node is not None:
	    yield from traverse(node.left):
        yield t.dat
	    yield from traverse(node.right)

Ce sera.

Réalisation de collout

Les sources suivantes ont des instructions détaillées: http://masnun.com/2015/11/13/python-generators-coroutines-native-coroutines-and-async-await.html

Un collout est un sous-programme qui vous permet de suspendre le traitement, puis de reprendre le traitement au milieu. Dans Corroutine, vous pouvez non seulement récupérer la valeur, mais aussi l'envoyer. Par exemple, prenez l'exemple suivant:

coroutine.py


def coro():
    hello = yield "Hello"
    yield hello
 
 
c = coro()
print(next(c))
print(c.send("World"))

Ici, vous pouvez voir que yield est sur le côté droit de l'expression d'affectation. Ensuite, la méthode d'envoi envoie la chaîne «" World "». Vous pouvez voir qu'il tire parti des caractéristiques des générateurs qui ont des états. Si vous voulez élaborer sur Corroutine, ce sera un autre article, donc je vais simplement le présenter ici. Si vous en avez envie, vous pouvez également publier un article sur Corroutine. De plus, puisque le colloutum natif a été implémenté à partir de python3.5, le même traitement peut être réalisé sans utiliser de générateur.

Résumé

Le générateur est un concept difficile à comprendre, mais je pense qu'il est étonnamment simple de le considérer comme un outil qui a un état et renvoie les valeurs dans l'ordre avec un traitement partiel seulement sans tenir la liste entière. Utilisons tous le générateur et écrivons des programmes sympas! (Bien que je ne l'ai pas encore maîtrisé).

Recommended Posts

J'ai essayé d'expliquer à quoi sert le générateur Python aussi facilement que possible.
J'ai essayé d'expliquer l'analyse de régression multiple aussi facilement que possible à l'aide d'exemples concrets.
J'ai essayé de savoir si ReDoS est possible avec Python
[Pyto] J'ai essayé d'utiliser un smartphone comme clavier pour PC
J'ai essayé d'implémenter le tri par fusion en Python avec le moins de lignes possible
J'ai essayé de créer facilement un système de présence entièrement automatique avec Selenium + Python
[Python] J'ai essayé d'obtenir le nom du type sous forme de chaîne de caractères à partir de la fonction type
J'ai essayé d'implémenter ce qui semble être un outil de snipper Windows avec Python
Je veux facilement implémenter le délai d'expiration en python
Je veux répéter plusieurs fois un générateur Python
J'ai essayé d'implémenter un pseudo pachislot en Python
Pour moi en tant que débutant Django (2) - Qu'est-ce que MTV?
J'ai essayé d'implémenter un automate cellulaire unidimensionnel en Python
[Chaîne de Markov] J'ai essayé de lire les citations en Python.
J'ai essayé "Comment obtenir une méthode décorée en Python"
Créer une interface graphique aussi facilement que possible avec python [édition tkinter]
J'ai essayé de créer un bot pour annoncer un événement Wiire
J'ai fait un chronomètre en utilisant tkinter avec python
[1 hour challenge] J'ai essayé de créer un site de bonne aventure qui soit trop adapté à Python
J'ai essayé "Streamlit" qui transforme le code Python en une application web tel quel
J'ai essayé de créer un générateur qui génère une classe conteneur C # à partir de CSV avec Python
J'ai aussi essayé d'imiter la fonction monade et la monade d'état avec le générateur en Python
[Python] Qu'est-ce qu'une fonction zip?
[Python] Qu'est-ce qu'une instruction with?
J'ai essayé de toucher Python (installation)
J'ai essayé d'expliquer l'ensemble de données de Pytorch
Python pour la déclaration ~ Qu'est-ce qui est itérable ~
À quoi sert le trait de soulignement Python (_)?
[5e] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai créé une bibliothèque qui lit facilement les fichiers de configuration avec Python
J'ai essayé de transformer un fichier Python en un EXE (erreur de récursivité prise en charge)
[2nd] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de créer une expression régulière de "montant" en utilisant Python
Ce à quoi j'étais accro en présentant ALE à Vim pour Python
[Python] J'ai essayé d'implémenter un tri stable, alors notez
J'ai essayé de créer une expression régulière de "temps" en utilisant Python
[3ème] J'ai essayé de créer un certain outil de type Authenticator avec python
[Python] Un mémo que j'ai essayé de démarrer avec asyncio
J'ai essayé de créer une liste de nombres premiers avec python
J'ai essayé de créer une expression régulière de "date" en utilisant Python
[Pandas] J'ai essayé d'analyser les données de ventes avec Python [Pour les débutants]
J'ai essayé de mettre en œuvre un jeu de dilemme de prisonnier mal compris en Python
J'ai essayé de faire un processus d'exécution périodique avec Selenium et Python
J'ai essayé de créer une application de notification de publication à 2 canaux avec Python
J'ai essayé de créer une application todo en utilisant une bouteille avec python
[4th] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de détecter facilement les points de repère du visage avec python et dlib
[1er] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de faire une étrange citation pour Jojo avec LSTM
[Python] J'ai essayé d'expliquer des mots difficiles à comprendre pour les débutants d'une manière facile à comprendre.
Python Learning Episode 4 de Mayungo: J'ai essayé de voir ce qui se passe lorsque les nombres sont traités comme des lettres
J'ai essayé de savoir si m est inclus dans ce qu'on appelle le type de plage ou une plage telle que n..m et plage (n, m)
J'ai essayé de créer un linebot (implémentation)
J'ai créé un conteneur Docker pour utiliser JUMAN ++, KNP, python (pour pyKNP).
J'ai essayé de résumer la gestion des exceptions Python
J'ai essayé d'implémenter PLSA en Python