L'instruction Python for est une instruction dite itérative, et est souvent utilisée pour extraire et traiter chaque élément de la liste dans l'ordre depuis le début.
La syntaxe est la suivante.
for x in [1, 2, 3, 4, 5]:
# ...
Si vous mettez la liste après, vous pouvez traiter en utilisant chaque élément.
En dehors de la liste, celles qui sont souvent utilisées sont «range (1, 6)» et «(1, 2, 3, 4, 5)».
«[1, 2, 3, 4, 5]», «range (1, 6)» et «(1, 2, 3, 4, 5)» sont tous 1 dans x lorsqu'ils sont appliqués à l'exemple de syntaxe ci-dessus. , 2, 3, 4, 5 sont attribués dans l'ordre.
Il est facile d'imaginer le flux de traitement de «[1, 2, 3, 4, 5]» et «(1, 2, 3, 4, 5)».
Cependant, il semble que «range (1, 6)» soit assigné à x même si 2 à 5 ne sont pas écrits.
Pourquoi.
"Range est une fonction, et si vous spécifiez 1 et 6 comme arguments,` [1, 2, 3, 4, 5] ʻest créé? "
Je comprends. Mais en réalité, ce qui est créé par la fonction range n'est pas une liste. Ce n'est pas non plus un tuple.
C'est un objet de portée.
Hmm ... c'est vrai ... alors?
... Ne vous demandez-vous pas pourquoi vous prenez la peine de créer un objet de plage? Vous ne vous souciez pas pourquoi c'est une liste? Est-ce vrai.
C'est plus intuitif pour les débutants d'être une liste, c'est donc juste un piège dans la croyance que "les utilisateurs n'ont pas à se soucier du contenu de la boîte noire". Mettons à jour notre intuition ici.
La raison de la création d'un objet range, ou l'idée généralisée du ** traitement itératif **, rendra la programmation amusante si vous la maîtrisez, alors maîtrisez-la.
Jusqu'à présent, nous avons spécifié trois objets différents après le in dans l'instruction for.
objets list, tuple et range.
Ces trois peuvent être tournés avec une instruction for.
Mais qu'en est-il du nombre 123456?
for i in 123456:
# ...
Malheureusement, j'obtiens une erreur. Quelle est cette différence?
Cherchez-vous à savoir si le gars qui analyse le code Python est un objet qui peut être tourné avec une instruction for?
Ce n'est pas faux, mais ce n'est pas entièrement expliqué.
Ensuite, vous décidez où chercher dans l'objet pour le tourner avec l'instruction for.
En fait, il y a quelque chose comme ** preuve ** qui indique un compagnon qui peut être activé dans l'instruction for, et lorsque vous le spécifiez après dans l'instruction for, vous regardez s'il existe cette preuve. Ces pairs sont appelés ici ** itérables **.
pour Bunman "Preuve Arne! OK!" list-chan "Merci!" tuple-chan "Merci!" range-chan "Merci!"
Alors quelle est la preuve?
Lors de l'utilisation de l'instruction for, appliquez en interne la ** fonction iter ** (décrite plus loin) au ** iterable ** spécifié après dans, et convertissez-le en ** iterator ** (décrit plus loin). Appliquez ensuite la fonction suivante à cet itateur ** autant que possible ** (discuté ci-dessous). C'est la situation interne de la déclaration for. (Il existe un autre modèle de circonstances internes, mais je vais l'omettre.)
??????
Si vous ne comprenez pas, prenez des notes et poursuivez votre lecture.
Un itérateur est un objet qui se renvoie lui-même lorsque la fonction iter est appliquée et une valeur lorsque la fonction suivante est appliquée.
La fonction iter appelle en interne la méthode «iter» de l'objet.
La fonction suivante appelle en interne la méthode __next__
de l'objet.
class MyIter:
def __iter__(self):
return self
def __next__(self):
return 1
my_iter = MyIter()
# iter(my_iter) == my_iter.__iter__() == my_iter
# next(my_iter) == my_iter.__next__()
Cela seul est déjà un itérateur. L'objet range est également un itérateur et possède une méthode __iter__
appropriée et une méthode __next__
.
En d'autres termes, un objet qui a à la fois la méthode __iter__
et la méthode __next__
est appelé un itérateur.
En d'autres termes, l'itérateur requiert qu'il ait une méthode «iter» et une méthode «next».
Cette idée de "exiger d'avoir une méthode XX" s'appelle une interface en Java ou un protocole en Swift. Si vous ne répondez pas à la demande, vous obtiendrez une erreur. N'oubliez pas que la ** contrainte ** requise des utilisateurs au niveau de la spécification du langage est importante dans diverses situations.
La fonction iter convertit la valeur passée en un ** itérateur **. En interne, il appelle la méthode «iter» de l'objet. Si c'est déjà un itérateur comme expliqué ci-dessus, il se retourne.
Les objets autres que les itérateurs peuvent également avoir des méthodes «iter».
list n'est pas un itérateur car il n'a pas de méthode __next__
, mais s'il a une méthode __iter__
et que la fonction iter est appliquée, il retourne un itérateur appelé ** list_iterator **.
Et ici, l'objet qui peut renvoyer un itérateur avec la méthode __iter__
est appelé ** iterable **. En d'autres termes, un objet qui peut être converti en un itérateur avec la fonction iter est appelé un itérable.
Oui, la liste est donc itérable.
L'itérateur lui-même est itérable car il se retourne dans la fonction iter.
J'expliquerai à nouveau les circonstances internes de la déclaration for.
L'instruction for applique la fonction iter à l'itérable spécifié après dans, le convertit en itérateur, puis applique la fonction ** next à cet itérateur autant que possible.
Appliquer autant que possible la fonction suivante signifie que si vous ne définissez pas de fin, vous êtes dans une boucle infinie et, dans de nombreux cas, vous implémentez une fin (il existe également des itérateurs infinis).
Avoir une fin signifie que chaque fois que vous utilisez la fonction suivante, l'état de l'objet se rapproche de la fin.
Voici un exemple concret.
list_iter = iter([1,2,3,4,5])
a = next(list_iter)
# a => 1
b = next(list_iter)
# b => 2
c = next(list_iter)
# c => 3
d = next(list_iter)
# d => 4
e = next(list_iter)
# e => 5
list est itérable, et lorsque la fonction iter est appliquée, elle retourne un itérateur appelé list_iterator.
Lorsque la fonction suivante est appliquée à list_iterator, il semble afficher l'élément en attente.
(Vous n'avez pas à vous soucier de l'implémentation ici, mais pour le moment, list_iterator a probablement un index à l'intérieur, et l'application de la fonction suivante renvoie les éléments de l'index actuel et ajoute l'index à + Sera 1.)
Le dernier élément «5» a été attribué à «e». Et si nous appliquons à nouveau la fonction suivante ici?
f = next(list_iter)
# => StopIteration
Une exception a été levée ** StopIteration **. Si vous ne gérez pas d'exceptions, le programme se terminera ici.
«Autant que possible» signifie jusqu'à ce que cette itération d'arrêt soit lancée.
En d'autres termes, à l'intérieur de l'instruction for, si une exception appelée StopIteration est levée, elle sortira du bloc.
Les itérateurs sont utiles à bien des égards, et il n'y a pas de raison unique.
Je pense qu'il existe différents contextes tels que la sémantique, l'évaluation des délais, l'efficacité de la mémoire, les contraintes et la généralisation.
Il est très difficile de tout expliquer, donc l'histoire la plus informative est l'avantage de l'efficacité de la mémoire. Il décrit également la sémantique que j'aime personnellement et les avantages de la généralisation.
Pour expliquer l'efficacité de la mémoire, il semble bon de prendre l'objet range comme exemple.
C'est la réponse à la première question, "Pourquoi vous souciez-vous de créer un objet de plage?"
Supposons que la fonction range crée une liste.
Dans ce cas, vous souhaitez le traiter dans une plage de 1 à 100 000 000.
Ensuite, la plage (1, 100000001) sera créée sous la forme d'une liste de «[1, 2, ..., 100000000]».
Cela met beaucoup de pression sur la mémoire. Si la taille du type int est de 64 bits, la taille de cette liste sera de 6 400 000 000 bits (= 800 000 000 octets ≒ 800 Mo). (Pas exactement, mais ça va certainement être un putain de gros)
La manière de résoudre ce problème de compression est de faire de range un itérateur.
Plus précisément, il a «valeur de départ», «valeur actuelle (= valeur de départ)» et «valeur de fin» à l'intérieur, et renvoie la «valeur actuelle» chaque fois que la fonction suivante est appelée et «actuelle». Vous pouvez le répéter simplement en ajoutant +1 à la valeur. Et si vous lancez ** StopIteration ** lorsque la "valeur actuelle" devient la "valeur de fin", l'instruction for se terminera. (Plus précisément, c'est exactement le même que le style de langage C pour l'instruction)
Je pense que la mise en œuvre ressemblera à ce qui suit. (Pas équivalent à range.)
class Range:
def __init__(self, start, end):
self.i = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.i == self.end:
raise StopIteration
else:
x = self.i
self.i += 1
return x
for i in Range(1, 100):
print(i)
# 1
# 2
# ...
# 99
#Terminez ici
La mémoire allouée par cet itérateur est d'environ 192 bits, y compris la taille des valeurs internes «self.i», «self.end», «x», etc. (C'est un gâchis parce qu'il est cassé de différentes manières, mais c'est certainement plus petit.)
Par rapport à la liste, la distance de la terre à la lune et la largeur du court de tennis sont différentes.
L'implémenter dans un itérateur économise souvent de la mémoire plutôt que de créer une nouvelle liste.
C'est un avantage de l'efficacité de la mémoire de l'itérateur.
Je pense personnellement qu'il est très important d'interpréter diverses choses comme répétables.
Par exemple, je souhaite que les éléments suivants soient itérables:
Ces éléments doivent être les uns après les autres dans l'instruction for. Je veux que vous sortiez. J'ai un ressenti.
Même dans ma propre classe, il y a de nombreuses situations où cela semble rafraîchissant si cela peut être retourné avec une instruction for.
Être capable de spécifier itérable comme argument d'une fonction ou de quelque chose le rend plus polyvalent.
Par exemple, tout itérable doit être convertible en liste. Il est fastidieux de définir chaque type comme list (items: dict), list (items: tuple), etc. Ce serait donc bien de pouvoir spécifier un type qui a une preuve d'itérable.
x = list(iterable)
En fait, la liste a une telle implémentation.
Comme exemple plus élégant, le tri en tas
sorted_list = list(heap)
Ne serait-ce pas beau si cela pouvait s'exprimer ainsi?
En fait, les fonctions Python spécifient souvent itérable. Il y a tellement de fonctions max, de fonctions min, de fonctions de carte, de méthodes de jointure str, de fonctions de réduction du module functools, etc.
Si vous avez l'idée abstraite d'être reproductible dans votre entreprise, on vous demandera peut-être: "Est-ce une sorte de logique métier itérable?" (Source)
Les jeux au tour par tour comme Othello peuvent également être considérés comme itérables. Par conséquent, l'implémentation (expression) suivante est possible.
othello = Othello()
for turn in othello:
point = input()
turn.put(point)
#Si le plateau est rempli de pierres ou se termine à mi-chemin, vous pouvez quitter l'instruction for en lançant Stop Iteration.
Le concept d'itérateur est commun à divers langages de programmation en tant que ** répéteur ** et est bien connu comme un «objet qui peut être spécifié dans un ** pour chaque instruction **». Normalement, vous ne connaissez pas les itérateurs, et vous avez tendance à penser que pour chaque instruction est une syntaxe préparée pour des choses limitées telles que ArrayList, mais en fait, dans n'importe quel langage, si vous implémentez une preuve (interface) qui peut être tournée avec pour chacun, votre propre classe Mais vous pouvez le tourner avec le pour chaque instruction.
Recommended Posts