Itérateur et générateur Python

Résumons l'itérateur et le générateur de Python.

(Ajout 2018.12.25: Complètement remplacé par la grammaire Python 3)

--Iterator: [Interface] qui vous permet de récupérer des éléments de manière itérative (https://docs.python.org/2/library/stdtypes.html#iterator-types) --Generator: Un type d'itérateur qui traite chaque élément lorsqu'il tente de le récupérer et génère l'élément. En Python, il semble qu'il se réfère souvent à l'implémentation à l'aide de l'instruction yield.

Toute collection intégrée Python (liste, tuple, ensemble, dict, etc.) peut être itérée, mais dans les cas suivants, il est nécessaire de mettre une valeur dans la collection à l'avance pour un traitement itératif à l'aide de la collection intégrée. Je pense qu'il y a des cas où vous souhaitez implémenter vous-même un itérateur ou un générateur.

Implémentation d'itérateurs par classe

Si vous placez un objet dans un contexte qui attend un itérateur, tel que for in, la méthode __iter__ () de l'objet est d'abord appelée, ce qui l'oblige à renvoyer une implémentation d'itérateur. L'objet obtenu par cette valeur de retour est appelé la méthode «next ()». __next__ () sera appelé jusqu'à ce que vous obteniez une exception StopIteration.

Ce n'est pas différent de list normalement, mais c'est un exemple d'implémentation qui renvoie une liste de nombres donnés au moment de l'instanciation dans l'ordre.

sample1.py


class MyIterator(object):
	def __init__(self, *numbers):
		self._numbers = numbers
		self._i = 0
	def __iter__(self):
		# __next__()Est implémenté par soi-même, donc il retourne le soi tel qu'il est
		return self
	def __next__(self):  #Suivant pour Python2(self)Défini dans
		if self._i == len(self._numbers):
			raise StopIteration()
		value = self._numbers[self._i]
		self._i += 1
		return value
        
my_iterator = MyIterator(10, 20, 30)
for num in my_iterator:
	print('hello %d' % num)

Le résultat est

hello 10 hello 20 hello 30

Sera.

Dans cet exemple, __iter__ () retourne self, mais lorsque le traitement de l'itération est susceptible d'être compliqué, une autre classe d'implémentation pour l'itération est implémentée pour créer un tel objet. Il est également possible de le retourner.

Si vous utilisez la fonction intégrée ʻiter () `, vous pouvez voir que les types intégrés tels que list sont également implémentés selon cette règle.

>>> hoge = [1, 2, 3]
>>> hoge_iter = iter(hoge)
>>> hoge_iter.__next__()
1
>>> hoge_iter.__next__()
2
>>> hoge_iter.__next__()
3
>>> hoge_iter.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Résumé de l'itérateur

--La méthode __iter__ () est appelée lorsqu'une itération est demandée pour un objet.

Implémentation d'un générateur utilisant yield

Il peut être difficile de comprendre le rendement si vous n'y êtes pas habitué, mais le mécanisme est simple. Il n'est pas nécessaire de définir une classe lors de l'implémentation d'un générateur utilisant yield. Définissons la fonction de générateur suivante.

my_generator.py



def my_generator():
	yield 1
	yield 2
	yield 3

Il s'agit d'un générateur qui génère trois valeurs, «1», «2» et «3» dans l'ordre. Notez que ** les instructions return ne peuvent pas être utilisées dans les fonctions du générateur **.

Les générateurs sont souvent utilisés dans les cas suivants où les coûts de calcul sont un problème.

Une fonction de générateur devient un objet itérateur en effectuant un appel de fonction.

gen = my_generator()
gen.__next__()  # 1
gen.__next__()  # 2
gen.__next__()  # 3
gen.__next__()  # StopIteration

Yield renvoie le contrôle au côté qui a appelé next (). Vérifions le flux de traitement avec l'instruction d'impression comme indiqué ci-dessous.

generator_sample.py



def my_generator():
	print('before yield')
	yield 1
	print('yielded 1')
	yield 2
	print('yielded 2')
	yield 3
	print('yielded 3, finished')

def main():
	gen = my_generator()
	print('start')
	v1 = gen.__next__()
	print('called __next__(), v1=%s' % v1)
	v2 = gen.__next__()
	print('called __next__(), v2=%s' % v2)
	v3 = gen.__next__()
	print('called __next__(), v3=%s' % v3)
	v4 = gen.__next__()  # should be exception

main()

Le résultat de l'exécution est le suivant.

start
before yield
called __next__(), v1=1
yielded 1
called __next__(), v2=2
yielded 2
called __next__(), v3=3
yielded 3, finished
Traceback (most recent call last):
  File "./generator_sample.py", line 21, in <module>
    main()
  File "./generator_sample.py", line 19, in main
    v4 = gen.__next__()  # should be exception
StopIteration

Résumé du générateur

--Utiliser le rendement dans la mise en œuvre du générateur

Notation et itérateur d'inclusion list / tuple / set / list

Les itérables peuvent être facilement liés à des fonctionnalités intégrées telles que la notation d'inclusion de liste / tuple / ensemble / liste.

Par exemple, vous pouvez facilement convertir le générateur simple ci-dessus en un objet liste avec une valeur telle que «[1, 2, 3]» en le passant à la fonction «list ()». La même chose est vraie pour tuple et set.


def my_generator():
	yield 1
	yield 2
	yield 3

def my_generator2():
	yield 10
	yield 10
	yield 20

print(list(my_generator()))  # => [1, 2, 3]
print([v * 2 for v in my_generator()])  # => [2, 4, 6]
print(set(my_generator2()))  # => set([10, 20])

Bien sûr, non seulement le générateur implémenté avec yield mais aussi l'itérateur implémenté avec __next__ () ʻet return` peuvent également être liés aux fonctions intégrées.

itertools

Présentation de Python car il existe une bibliothèque appelée itertools qui vous permet d'effectuer facilement diverses opérations en combinant des objets iter. Je vais le laisser. Je pense que cela est principalement utilisé pour les données intégrées telles que liste / tuple sans implémenter d'itérateur par vous-même, donc c'est pratique même si vous n'implémentez pas d'itérateur.

Par exemple, il est facile de [énumérer toutes les combinaisons] de [1, 2, 3] et ['a', 'b'] (http://qiita.com/tomotaka_ito/items/5a545423eac654a5b6f5). Je peux le faire.

more_itertools

Il existe également une bibliothèque PyPI non standard appelée more_itertools. Il contient de nombreuses fonctions utiles qui ne sont pas incluses dans itertools. Il y a «chunked» qui regroupe tous les N morceaux et «silen» qui compte le nombre en tournant l'itérateur.

Vous devez l'installer pour l'utiliser.

$ pip install more-itertools

Il y a aussi article Qiita qui présente et explique itertools / more_itertools, donc je pense que ce sera utile.

Utilisez le générateur encore et encore

Une fois que le générateur est tourné dans la boucle «for», les éléments n'apparaîtront pas dans la deuxième boucle «for» et les suivantes.

Si vous voulez pouvoir appeler la fonction générateur autant de fois que vous le souhaitez sans aucun effet secondaire, Il peut être utile d'utiliser la technique décrite dans Je veux réitérer le générateur Python plusieurs fois.

Résumé

Lien de référence

Recommended Posts

Itérateur et générateur Python
Notation et générateur d'inclusion de liste Python
[Python] Une compréhension approximative des itérables, des itérateurs et des générateurs
Générez des nombres de Fibonacci avec des fermetures, des itérateurs et des générateurs Python
Passons en revue les spécifications du langage autour des itérateurs et des générateurs Python
[python] Compresser et décompresser
Astuces Python et Numpy
[Python] pip et roue
Paquets et modules Python
Intégration Vue-Cli et Python
Ruby, Python et carte
entrée et sortie python
Python et Ruby se séparent
Python asyncio et ContextVar
Programmation avec Python et Tkinter
Chiffrement et déchiffrement avec Python
Python: variables de classe et d'instance
3-3, chaîne Python et code de caractère
Série Python 2 et série 3 (édition Anaconda)
Python sur Ruby et Ruby en colère sur Python
Indentation Python et format de chaîne
division des nombres réels python (/) et division des nombres entiers (//)
Installez Python et Flask (Windows 10)
À propos des objets et des classes Python
À propos des variables et des objets Python
Apache mod_auth_tkt et Python AuthTkt
Å (Ongustorome) et NFC @ Python
Apprenez à connaître les packages et les modules Python
# 2 [python3] Séparation et commentaire
Copie superficielle Python et copie profonde
Mémo tranche python et rubis
Installation de Python et grammaire de base
J'ai comparé Java et Python!
Copie superficielle Python et copie profonde
À propos de Python, len () et randint ()
À propos de la date et du fuseau horaire Python
Installez Python 3.7 et Django 3.0 (CentOS)
Construction d'environnement Python et TensorFlow
Variables de classe et d'instance Python
Syntaxe Ruby et Python ~ branch ~
[Python] Python et sécurité-① Qu'est-ce que Python?
Pile et file d'attente en Python
métaclasse python et déclaration sqlalchemy
Implémentation de Fibonacci et des nombres premiers (python)
bases de python: conditions et itérations
Opérateur de bits Python et somme logique
Module de débogage et de test Python
Liste Python et tapples et virgules
Variables Python et ID d'objet
À propos de Python et des expressions régulières
python avec pyenv et venv
Unittest et CI en Python
Description et implémentation de Maxout (Python)
[python] Obtenir le quotient et le surplus
Fonctions de tri et de comparaison Python 3
[Python] Recherche de priorité de profondeur et recherche de priorité de largeur
Identité et équivalence: is et == en Python
Installation source et installation de Python
Python ou et et opérateur trap