Introduction
Cette section examine le processus sous-jacent d'itération.
This tutorial is from open-source community. Access the source code
Cette section examine le processus sous-jacent d'itération.
De nombreux objets différents prennent en charge l'itération.
a = 'hello'
for c in a: ## Itérer sur les caractères de a
...
b = { 'name': 'Dave', 'password': 'foo'}
for k in b: ## Itérer sur les clés dans le dictionnaire
...
c = [1,2,3,4]
for i in c: ## Itérer sur les éléments d'une liste/tuple
...
f = open('foo.txt')
for x in f: ## Itérer sur les lignes d'un fichier
...
Considérez l'instruction for.
for x in obj:
## instructions
Que se passe-t-il en dessous des couvertures?
_iter = obj.__iter__() ## Obtenir l'objet itérateur
while True:
try:
x = _iter.__next__() ## Obtenir l'élément suivant
## instructions...
except StopIteration: ## Plus d'éléments
break
Tous les objets qui fonctionnent avec la boucle for implémentent ce protocole d'itération de bas niveau.
Exemple : Itération manuelle sur une liste.
>>> x = [1,2,3]
>>> it = x.__iter__()
>>> it
<listiterator object at 0x590b0>
>>> it.__next__()
1
>>> it.__next__()
2
>>> it.__next__()
3
>>> it.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in? StopIteration
>>>
Il est utile de connaître l'itération si vous voulez l'ajouter à vos propres objets. Par exemple, pour créer un conteneur personnalisé.
class Portfolio:
def __init__(self):
self.holdings = []
def __iter__(self):
return self.holdings.__iter__()
...
port = Portfolio()
for s in port:
...
Créez la liste suivante :
a = [1,9,4,25,16]
Itérez manuellement sur cette liste. Appelez __iter__() pour obtenir un itérateur et appelez la méthode __next__() pour obtenir les éléments successifs.
>>> i = a.__iter__()
>>> i
<listiterator object at 0x64c10>
>>> i.__next__()
1
>>> i.__next__()
9
>>> i.__next__()
4
>>> i.__next__()
25
>>> i.__next__()
16
>>> i.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
La fonction intégrée next() est un raccourci pour appeler la méthode __next__() d'un itérateur. Essayez de l'utiliser sur un fichier :
>>> f = open('portfolio.csv')
>>> f.__iter__() ## Note : Cela renvoie le fichier lui-même
<_io.TextIOWrapper name='portfolio.csv' mode='r' encoding='UTF-8'>
>>> next(f)
'name,shares,price\n'
>>> next(f)
'"AA",100,32.20\n'
>>> next(f)
'"IBM",50,91.10\n'
>>>
Appelez next(f) jusqu'à la fin du fichier. Regardez ce qui se passe.
Parfois, vous souhaiterez peut-être que l'un de vos propres objets prenne en charge l'itération - en particulier si votre objet encapsule une liste existante ou un autre itérable. Dans un nouveau fichier portfolio.py, définissez la classe suivante :
## portfolio.py
class Portfolio:
def __init__(self, holdings):
self._holdings = holdings
@property
def total_cost(self):
return sum([s.cost for s in self._holdings])
def tabulate_shares(self):
from collections import Counter
total_shares = Counter()
for s in self._holdings:
total_shares[s.name] += s.shares
return total_shares
Cette classe est censée être une couche autour d'une liste, mais avec quelques méthodes supplémentaires telles que la propriété total_cost. Modifiez la fonction read_portfolio() dans report.py de manière à ce qu'elle crée une instance de Portfolio comme ceci :
## report.py
...
import fileparse
from stock import Stock
from portfolio import Portfolio
def read_portfolio(filename):
'''
Lit un fichier de portefeuille d'actions dans une liste de dictionnaires avec les clés
name, shares et price.
'''
with open(filename) as file:
portdicts = fileparse.parse_csv(file,
select=['name','shares','price'],
types=[str,int,float])
portfolio = [ Stock(d['name'], d['shares'], d['price']) for d in portdicts ]
return Portfolio(portfolio)
...
Essayez d'exécuter le programme report.py. Vous constaterez qu'il échoue de manière spectaculaire en raison du fait que les instances de Portfolio ne sont pas itérables.
>>> import report
>>> report.portfolio_report('portfolio.csv', 'prices.csv')
... plante...
Corrigez ce problème en modifiant la classe Portfolio pour qu'elle prenne en charge l'itération :
class Portfolio:
def __init__(self, holdings):
self._holdings = holdings
def __iter__(self):
return self._holdings.__iter__()
@property
def total_cost(self):
return sum([s.shares*s.price for s in self._holdings])
def tabulate_shares(self):
from collections import Counter
total_shares = Counter()
for s in self._holdings:
total_shares[s.name] += s.shares
return total_shares
Après avoir effectué ce changement, votre programme report.py devrait fonctionner à nouveau. Pendant que vous y êtes, corrigez votre programme pcost.py pour utiliser le nouveau Portfolio objet. Comme ceci :
## pcost.py
import report
def portfolio_cost(filename):
'''
Calcule le coût total (shares*price) d'un fichier de portefeuille
'''
portfolio = report.read_portfolio(filename)
return portfolio.total_cost
...
Testez-le pour vous assurer qu'il fonctionne :
>>> import pcost
>>> pcost.portfolio_cost('portfolio.csv')
44671.15
>>>
Lorsque vous créez une classe de conteneur, vous voulez souvent faire plus que simplement itérer. Modifiez la classe Portfolio de sorte qu'elle ait d'autres méthodes spéciales comme ceci :
class Portfolio:
def __init__(self, holdings):
self._holdings = holdings
def __iter__(self):
return self._holdings.__iter__()
def __len__(self):
return len(self._holdings)
def __getitem__(self, index):
return self._holdings[index]
def __contains__(self, name):
return any([s.name == name for s in self._holdings])
@property
def total_cost(self):
return sum([s.shares*s.price for s in self._holdings])
def tabulate_shares(self):
from collections import Counter
total_shares = Counter()
for s in self._holdings:
total_shares[s.name] += s.shares
return total_shares
Maintenant, essayez quelques expériences avec cette nouvelle classe :
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> len(portfolio)
7
>>> portfolio[0]
Stock('AA', 100, 32.2)
>>> portfolio[1]
Stock('IBM', 50, 91.1)
>>> portfolio[0:3]
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44)]
>>> 'IBM' in portfolio
True
>>> 'AAPL' in portfolio
False
>>>
Une observation importante à propos de ceci - généralement, le code est considéré comme "Pythonique" s'il utilise le vocabulaire commun de la manière dont les autres parties de Python fonctionnent normalement. Pour les objets conteneurs, prendre en charge l'itération, l'indexation, la containment et d'autres types d'opérateurs est une partie importante de cela.
Félicitations ! Vous avez terminé le laboratoire sur le protocole d'itération. Vous pouvez pratiquer d'autres laboratoires sur LabEx pour améliorer vos compétences.