Itérer comme un pro

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce laboratoire (lab), vous allez apprendre le concept fondamental d'itération en programmation Python. L'itération vous permet de traiter efficacement les éléments de séquences telles que les listes, les tuples et les dictionnaires. Maîtriser les techniques d'itération peut améliorer considérablement vos compétences en codage Python.

Vous allez explorer plusieurs techniques d'itération puissantes en Python, y compris l'itération de base avec une boucle for, le déballage de séquences, l'utilisation de fonctions intégrées telles que enumerate() et zip(), et l'utilisation d'expressions génératrices pour une meilleure efficacité mémoire.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ControlFlowGroup(["Control Flow"]) python(("Python")) -.-> python/DataStructuresGroup(["Data Structures"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python(("Python")) -.-> python/PythonStandardLibraryGroup(["Python Standard Library"]) python/ControlFlowGroup -.-> python/for_loops("For Loops") python/DataStructuresGroup -.-> python/lists("Lists") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/build_in_functions("Build-in Functions") python/AdvancedTopicsGroup -.-> python/generators("Generators") python/PythonStandardLibraryGroup -.-> python/data_collections("Data Collections") subgraph Lab Skills python/for_loops -.-> lab-132442{{"Itérer comme un pro"}} python/lists -.-> lab-132442{{"Itérer comme un pro"}} python/function_definition -.-> lab-132442{{"Itérer comme un pro"}} python/build_in_functions -.-> lab-132442{{"Itérer comme un pro"}} python/generators -.-> lab-132442{{"Itérer comme un pro"}} python/data_collections -.-> lab-132442{{"Itérer comme un pro"}} end

Itération de base et déballage de séquences

Dans cette étape, nous allons explorer l'itération de base à l'aide de boucles for et le déballage de séquences en Python. L'itération est un concept fondamental en programmation, qui vous permet de parcourir chaque élément d'une séquence un par un. Le déballage de séquences, quant à lui, vous permet d'assigner les éléments individuels d'une séquence à des variables de manière pratique.

Chargement de données depuis un fichier CSV

Commençons par charger des données depuis un fichier CSV. CSV (Comma-Separated Values, valeurs séparées par des virgules) est un format de fichier courant utilisé pour stocker des données tabulaires. Pour commencer, nous devons ouvrir un terminal dans le WebIDE et démarrer l'interpréteur Python. Cela nous permettra d'exécuter du code Python de manière interactive.

cd ~/project
python3

Maintenant que nous sommes dans l'interpréteur Python, nous pouvons exécuter le code Python suivant pour lire les données depuis le fichier portfolio.csv. Tout d'abord, nous importons le module csv, qui fournit des fonctionnalités pour travailler avec les fichiers CSV. Ensuite, nous ouvrons le fichier et créons un objet csv.reader pour lire les données. Nous utilisons la fonction next pour obtenir les en-têtes de colonne, et nous convertissons les données restantes en une liste. Enfin, nous utilisons la fonction pprint du module pprint pour afficher les lignes d'une manière plus lisible.

import csv

f = open('portfolio.csv')
f_csv = csv.reader(f)
headers = next(f_csv)    ## Obtenir les en-têtes de colonne
rows = list(f_csv)       ## Convertir les données restantes en une liste
from pprint import pprint
pprint(rows)             ## Afficher les lignes de manière lisible

Vous devriez voir une sortie similaire à ceci :

[['AA', '100', '32.20'],
 ['IBM', '50', '91.10'],
 ['CAT', '150', '83.44'],
 ['MSFT', '200', '51.23'],
 ['GE', '95', '40.37'],
 ['MSFT', '50', '65.10'],
 ['IBM', '100', '70.44']]

Itération de base avec des boucles for

La déclaration for en Python est utilisée pour itérer sur n'importe quelle séquence de données, comme une liste, un tuple ou une chaîne de caractères. Dans notre cas, nous l'utiliserons pour itérer sur les lignes de données que nous avons chargées depuis le fichier CSV.

for row in rows:
    print(row)

Ce code parcourra chaque ligne de la liste rows et l'affichera. Vous verrez chaque ligne de données de notre fichier CSV affichée une par une.

['AA', '100', '32.20']
['IBM', '50', '91.10']
['CAT', '150', '83.44']
['MSFT', '200', '51.23']
['GE', '95', '40.37']
['MSFT', '50', '65.10']
['IBM', '100', '70.44']

Déballage de séquences dans les boucles

Python vous permet de déballer des séquences directement dans une boucle for. Cela est très utile lorsque vous connaissez la structure de chaque élément de la séquence. Dans notre cas, chaque ligne de la liste rows contient trois éléments : un nom, le nombre d'actions et le prix. Nous pouvons déballer ces éléments directement dans la boucle for.

for name, shares, price in rows:
    print(name, shares, price)

Ce code déballera chaque ligne dans les variables name, shares et price, puis les affichera. Vous verrez les données affichées dans un format plus lisible.

AA 100 32.20
IBM 50 91.10
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.10
IBM 100 70.44

Si vous n'avez pas besoin de certaines valeurs, vous pouvez utiliser _ comme espace réservé pour indiquer que vous n'avez pas besoin de ces valeurs. Par exemple, si vous ne voulez afficher que le nom et le prix, vous pouvez utiliser le code suivant :

for name, _, price in rows:
    print(name, price)

Ce code ignorera le deuxième élément de chaque ligne et n'affichera que le nom et le prix.

AA 32.20
IBM 91.10
CAT 83.44
MSFT 51.23
GE 40.37
MSFT 65.10
IBM 70.44

Déballage étendu avec l'opérateur *

Pour un déballage plus avancé, vous pouvez utiliser l'opérateur * comme joker. Cela vous permet de collecter plusieurs éléments dans une liste. Groupons nos données par nom en utilisant cette technique.

from collections import defaultdict

byname = defaultdict(list)
for name, *data in rows:
    byname[name].append(data)

## Afficher les données pour IBM
print(byname['IBM'])

## Itérer à travers les données d'IBM
for shares, price in byname['IBM']:
    print(shares, price)

Dans ce code, nous importons d'abord la classe defaultdict du module collections. Un defaultdict est un dictionnaire qui crée automatiquement une nouvelle valeur (dans ce cas, une liste vide) si la clé n'existe pas. Ensuite, nous utilisons l'opérateur * pour collecter tous les éléments sauf le premier dans une liste appelée data. Nous stockons cette liste dans le dictionnaire byname, groupée par nom. Enfin, nous affichons les données pour IBM et nous les parcourons pour afficher le nombre d'actions et le prix.

Sortie :

[['50', '91.10'], ['100', '70.44']]
50 91.10
100 00.44

Dans cet exemple, *data collecte tous les éléments sauf le premier dans une liste, que nous stockons ensuite dans un dictionnaire groupé par nom. C'est une technique puissante pour gérer des données avec des séquences de longueur variable.

Utilisation des fonctions enumerate() et zip()

Dans cette étape, nous allons explorer deux fonctions intégrées incroyablement utiles en Python, essentielles pour l'itération : enumerate() et zip(). Ces fonctions peuvent considérablement simplifier votre code lorsque vous travaillez avec des séquences.

Compter avec enumerate()

Lorsque vous itérez sur une séquence, vous aurez souvent besoin de suivre l'index ou la position de chaque élément. C'est là que la fonction enumerate() est pratique. La fonction enumerate() prend une séquence en entrée et renvoie des paires (index, valeur) pour chaque élément de cette séquence.

Si vous avez suivi dans l'interpréteur Python de l'étape précédente, vous pouvez continuer à l'utiliser. Sinon, démarrez une nouvelle session. Voici comment vous pouvez configurer les données si vous commencez à zéro :

## Si vous démarrez une nouvelle session, rechargez d'abord les données :
## import csv
## f = open('portfolio.csv')
## f_csv = csv.reader(f)
## headers = next(f_csv)
## rows = list(f_csv)

## Utilisez enumerate pour obtenir les numéros de ligne
for rowno, row in enumerate(rows):
    print(rowno, row)

Lorsque vous exécutez le code ci - dessus, la fonction enumerate(rows) générera des paires d'un index (commençant à 0) et de la ligne correspondante de la séquence rows. La boucle for déballera ensuite ces paires dans les variables rowno et row, et nous les afficherons.

Sortie :

0 ['AA', '100', '32.20']
1 ['IBM', '50', '91.10']
2 ['CAT', '150', '83.44']
3 ['MSFT', '200', '51.23']
4 ['GE', '95', '40.37']
5 ['MSFT', '50', '65.10']
6 ['IBM', '100', '70.44']

Nous pouvons rendre le code encore plus lisible en combinant enumerate() avec le déballage. Le déballage nous permet d'assigner directement les éléments d'une séquence à des variables individuelles.

for rowno, (name, shares, price) in enumerate(rows):
    print(rowno, name, shares, price)

Dans ce code, nous utilisons une paire supplémentaire de parenthèses autour de (name, shares, price) pour déballer correctement les données de la ligne. enumerate(rows) nous donne toujours l'index et la ligne, mais maintenant nous déballons la ligne dans les variables name, shares et price.

Sortie :

0 AA 100 32.20
1 IBM 50 91.10
2 CAT 150 83.44
3 MSFT 200 51.23
4 GE 95 40.37
5 MSFT 50 65.10
6 IBM 100 70.44

Mise en paires de données avec zip()

La fonction zip() est un autre outil puissant en Python. Elle est utilisée pour combiner les éléments correspondants de plusieurs séquences. Lorsque vous passez plusieurs séquences à zip(), elle crée un itérateur qui produit des tuples, où chaque tuple contient des éléments de chacune des séquences d'entrée à la même position.

Voyons comment nous pouvons utiliser zip() avec les données headers et row avec lesquelles nous avons travaillé.

## Rappelez-vous la variable headers de plus tôt
print(headers)  ## Devrait afficher ['name', 'shares', 'price']

## Obtenez la première ligne
row = rows[0]
print(row)      ## Devrait afficher ['AA', '100', '32.20']

## Utilisez zip pour associer les noms de colonnes aux valeurs
for col, val in zip(headers, row):
    print(col, val)

Dans ce code, zip(headers, row) prend la séquence headers et la séquence row et met en paires leurs éléments correspondants. La boucle for déballera ensuite ces paires dans col (pour le nom de colonne de headers) et val (pour la valeur de row), et nous les afficherons.

Sortie :

['name', 'shares', 'price']
['AA', '100', '32.20']
name AA
shares 100
price 32.20

Un usage très courant de zip() est de créer des dictionnaires à partir de paires clé - valeur. En Python, un dictionnaire est une collection de paires clé - valeur.

## Créez un dictionnaire à partir des en-têtes et des valeurs de ligne
record = dict(zip(headers, row))
print(record)

Ici, zip(headers, row) crée des paires de noms de colonnes et de valeurs, et la fonction dict() prend ces paires et les transforme en un dictionnaire.

Sortie :

{'name': 'AA', 'shares': '100', 'price': '32.20'}

Nous pouvons étendre cette idée pour convertir toutes les lignes de notre séquence rows en dictionnaires.

## Convertissez toutes les lignes en dictionnaires
for row in rows:
    record = dict(zip(headers, row))
    print(record)

Dans cette boucle, pour chaque ligne de rows, nous utilisons zip(headers, row) pour créer des paires clé - valeur, puis dict() pour transformer ces paires en un dictionnaire. Cette technique est très courante dans les applications de traitement de données, en particulier lorsque vous travaillez avec des fichiers CSV où la première ligne contient les en-têtes.

Sortie :

{'name': 'AA', 'shares': '100', 'price': '32.20'}
{'name': 'IBM', 'shares': '50', 'price': '91.10'}
{'name': 'CAT', 'shares': '150', 'price': '83.44'}
{'name': 'MSFT', 'shares': '200', 'price': '51.23'}
{'name': 'GE', 'shares': '95', 'price': '40.37'}
{'name': 'MSFT', 'shares': '50', 'price': '65.10'}
{'name': 'IBM', 'shares': '100', 'price': '70.44'}

Expressions génératrices et efficacité mémoire

Dans cette étape, nous allons explorer les expressions génératrices. Elles sont incroyablement utiles lorsque vous travaillez avec de grands ensembles de données en Python. Elles peuvent rendre votre code beaucoup plus efficace en termes de mémoire, ce qui est crucial lorsque vous manipulez de grandes quantités de données.

Comprendre les expressions génératrices

Une expression génératrice est similaire à une compréhension de liste, mais il y a une différence clé. Lorsque vous utilisez une compréhension de liste, Python crée une liste avec tous les résultats d'un coup. Cela peut prendre beaucoup de mémoire, surtout si vous travaillez avec un grand ensemble de données. En revanche, une expression génératrice produit les résultats un par un au fur et à mesure qu'ils sont nécessaires. Cela signifie qu'elle n'a pas besoin de stocker tous les résultats en mémoire d'un coup, ce qui peut économiser une quantité significative de mémoire.

Regardons un exemple simple pour voir comment cela fonctionne :

## Démarrez une nouvelle session Python si nécessaire
## python3

## Compréhension de liste (crée une liste en mémoire)
nums = [1, 2, 3, 4, 5]
squares_list = [x*x for x in nums]
print(squares_list)

## Expression génératrice (crée un objet générateur)
squares_gen = (x*x for x in nums)
print(squares_gen)  ## Cela n'affiche pas les valeurs, seulement l'objet générateur

## Itérez à travers le générateur pour obtenir les valeurs
for n in squares_gen:
    print(n)

Lorsque vous exécutez ce code, vous verrez la sortie suivante :

[1, 4, 9, 16, 25]
<generator object <genexpr> at 0x7f...>
1
4
9
16
25

Une chose importante à noter sur les générateurs est qu'ils ne peuvent être itérés qu'une seule fois. Une fois que vous avez parcouru toutes les valeurs d'un générateur, il est épuisé et vous ne pouvez plus obtenir les valeurs.

## Essayez d'itérer à nouveau sur le même générateur
for n in squares_gen:
    print(n)  ## Rien ne sera affiché, car le générateur est déjà épuisé

Vous pouvez également obtenir manuellement les valeurs d'un générateur une par une en utilisant la fonction next().

## Créez un nouveau générateur
squares_gen = (x*x for x in nums)

## Obtenez les valeurs une par une
print(next(squares_gen))  ## 1
print(next(squares_gen))  ## 4
print(next(squares_gen))  ## 9

Lorsqu'il n'y a plus de valeurs dans le générateur, l'appel de next() lèvera une exception StopIteration.

Fonctions génératrices avec yield

Pour une logique de génération plus complexe, vous pouvez écrire des fonctions génératrices en utilisant l'instruction yield. Une fonction génératrice est un type spécial de fonction qui utilise yield pour retourner des valeurs une par une au lieu de retourner un seul résultat d'un coup.

def squares(nums):
    for x in nums:
        yield x*x

## Utilisez la fonction génératrice
for n in squares(nums):
    print(n)

Lorsque vous exécutez ce code, vous verrez la sortie suivante :

1
4
9
16
25

Réduire l'utilisation de mémoire avec les expressions génératrices

Maintenant, voyons comment les expressions génératrices peuvent économiser de la mémoire lorsqu'elles sont utilisées avec de grands ensembles de données. Nous allons utiliser le fichier de données des bus CTA, qui est assez volumineux.

Tout d'abord, essayons une approche gourmande en mémoire :

import tracemalloc
tracemalloc.start()

import readrides
rows = readrides.read_rides_as_dicts('ctabus.csv')
rt22 = [row for row in rows if row['route'] == '22']
max_row = max(rt22, key=lambda row: int(row['rides']))
print(max_row)

## Vérifiez l'utilisation de mémoire
current, peak = tracemalloc.get_traced_memory()
print(f"Utilisation mémoire actuelle : {current / 1024 / 1024:.2f} MB")
print(f"Utilisation mémoire maximale : {peak / 1024 / 1024:.2f} MB")

Maintenant, quittez Python et redémarrez - le pour comparer avec une approche basée sur les générateurs :

exit() python3
import tracemalloc
tracemalloc.start()

import csv
f = open('ctabus.csv')
f_csv = csv.reader(f)
headers = next(f_csv)

## Utilisez des expressions génératrices pour toutes les opérations
rows = (dict(zip(headers, row)) for row in f_csv)
rt22 = (row for row in rows if row['route'] == '22')
max_row = max(rt22, key=lambda row: int(row['rides']))
print(max_row)

## Vérifiez l'utilisation de mémoire
current, peak = tracemalloc.get_traced_memory()
print(f"Utilisation mémoire actuelle : {current / 1024 / 1024:.2f} MB")
print(f"Utilisation mémoire maximale : {peak / 1024 / 1024:.2f} MB")

Vous devriez remarquer une différence significative dans l'utilisation de mémoire entre ces deux approches. L'approche basée sur les générateurs traite les données de manière incrémentielle sans charger tout en mémoire d'un coup, ce qui est beaucoup plus efficace en termes de mémoire.

Expressions génératrices avec des fonctions de réduction

Les expressions génératrices sont particulièrement utiles lorsqu'elles sont combinées avec des fonctions telles que sum(), min(), max(), any() et all() qui traitent une séquence entière et produisent un seul résultat.

Regardons quelques exemples :

from readport import read_portfolio
portfolio = read_portfolio('portfolio.csv')

## Calculez la valeur totale du portefeuille
total_value = sum(s['shares']*s['price'] for s in portfolio)
print(f"Valeur totale du portefeuille : {total_value}")

## Trouvez le nombre minimum d'actions dans n'importe quelle holding
min_shares = min(s['shares'] for s in portfolio)
print(f"Nombre minimum d'actions dans n'importe quelle holding : {min_shares}")

## Vérifiez si une action est IBM
has_ibm = any(s['name'] == 'IBM' for s in portfolio)
print(f"Le portefeuille contient IBM : {has_ibm}")

## Vérifiez si toutes les actions sont IBM
all_ibm = all(s['name'] == 'IBM' for s in portfolio)
print(f"Toutes les actions sont IBM : {all_ibm}")

## Comptez le nombre d'actions IBM
ibm_shares = sum(s['shares'] for s in portfolio if s['name'] == 'IBM')
print(f"Nombre total d'actions IBM : {ibm_shares}")

Les expressions génératrices sont également utiles pour les opérations sur les chaînes de caractères. Voici comment joindre les valeurs d'un tuple :

s = ('GOOG', 100, 490.10)
## Cela échouerait : ','.join(s)
## Utilisez une expression génératrice pour convertir tous les éléments en chaînes de caractères
joined = ','.join(str(x) for x in s)
print(joined)  ## Sortie : 'GOOG,100,490.1'

L'avantage clé de l'utilisation d'expressions génératrices dans ces exemples est qu'aucune liste intermédiaire n'est créée, ce qui entraîne un code plus efficace en termes de mémoire.

Résumé

Dans ce laboratoire, vous avez appris plusieurs techniques d'itération puissantes en Python. Tout d'abord, vous avez maîtrisé l'itération de base et le déballage de séquences, en utilisant des boucles for pour itérer sur des séquences et les déballer dans des variables individuelles. Ensuite, vous avez exploré des fonctions intégrées telles que enumerate() pour suivre les indices lors de l'itération et zip() pour associer des éléments de différentes séquences.

Ces techniques sont fondamentales pour une programmation Python efficace. Elles vous permettent d'écrire un code plus concis, plus lisible et plus efficace en termes de mémoire. En maîtrisant ces modèles d'itération, vous pouvez gérer plus efficacement les tâches de traitement de données dans vos projets Python.