Aspects définitionnels des fonctions

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, vous apprendrez à explorer les aspects fondamentaux des fonctions et des méthodes Python. Vous rendrez également les fonctions plus flexibles en concevant efficacement des paramètres.

De plus, vous implémenterez des indications de type (type hints) pour améliorer la lisibilité et la sécurité du code, ce qui est crucial pour écrire un code Python de haute qualité.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/PythonStandardLibraryGroup(["Python Standard Library"]) python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/FileHandlingGroup(["File Handling"]) python/BasicConceptsGroup -.-> python/variables_data_types("Variables and Data Types") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/FunctionsGroup -.-> python/default_arguments("Default Arguments") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/FileHandlingGroup -.-> python/file_opening_closing("Opening and Closing Files") python/FileHandlingGroup -.-> python/file_reading_writing("Reading and Writing Files") python/FileHandlingGroup -.-> python/file_operations("File Operations") python/PythonStandardLibraryGroup -.-> python/data_collections("Data Collections") subgraph Lab Skills python/variables_data_types -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/function_definition -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/arguments_return -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/default_arguments -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/classes_objects -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/file_opening_closing -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/file_reading_writing -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/file_operations -.-> lab-132503{{"Aspects définitionnels des fonctions"}} python/data_collections -.-> lab-132503{{"Aspects définitionnels des fonctions"}} end

Comprendre le contexte

Dans les exercices précédents, vous avez peut - être rencontré du code qui lit des fichiers CSV et stocke les données dans diverses structures de données. Le but de ce code est de prendre les données textuelles brutes d'un fichier CSV et de les convertir en objets Python plus utiles, tels que des dictionnaires ou des instances de classe. Cette conversion est essentielle car elle nous permet de travailler avec les données de manière plus structurée et significative dans nos programmes Python.

Le modèle typique pour lire des fichiers CSV suit souvent une structure spécifique. Voici un exemple de fonction qui lit un fichier CSV et convertit chaque ligne en un dictionnaire :

import csv

def read_csv_as_dicts(filename, types):
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = { name: func(val)
                       for name, func, val in zip(headers, types, row) }
            records.append(record)
    return records

Analysons le fonctionnement de cette fonction. Tout d'abord, elle importe le module csv, qui fournit des fonctionnalités pour travailler avec les fichiers CSV en Python. La fonction prend deux paramètres : filename, qui est le nom du fichier CSV à lire, et types, qui est une liste de fonctions utilisées pour convertir les données de chaque colonne en le type de données approprié.

À l'intérieur de la fonction, elle initialise une liste vide appelée records pour stocker les dictionnaires représentant chaque ligne du fichier CSV. Elle ouvre ensuite le fichier à l'aide de l'instruction with, qui garantit que le fichier est correctement fermé après l'exécution du bloc de code. La fonction csv.reader est utilisée pour créer un itérateur qui lit chaque ligne du fichier CSV. On suppose que la première ligne est l'en - tête, donc elle est récupérée à l'aide de la fonction next.

Ensuite, la fonction itère sur les lignes restantes du fichier CSV. Pour chaque ligne, elle crée un dictionnaire à l'aide d'une compréhension de dictionnaire. Les clés du dictionnaire sont les en - têtes de colonne, et les valeurs sont le résultat de l'application de la fonction de conversion de type correspondante de la liste types à la valeur de la ligne. Enfin, le dictionnaire est ajouté à la liste records, et la fonction retourne la liste de dictionnaires.

Maintenant, regardons une fonction similaire qui lit les données d'un fichier CSV dans des instances de classe :

def read_csv_as_instances(filename, cls):
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = cls.from_row(row)
            records.append(record)
    return records

Cette fonction est similaire à la précédente, mais au lieu de créer des dictionnaires, elle crée des instances d'une classe. La fonction prend deux paramètres : filename, qui est le nom du fichier CSV à lire, et cls, qui est la classe dont les instances seront créées.

À l'intérieur de la fonction, elle suit une structure similaire à la fonction précédente. Elle initialise une liste vide appelée records pour stocker les instances de classe. Elle ouvre ensuite le fichier, lit les en - têtes et itère sur les lignes restantes. Pour chaque ligne, elle appelle la méthode from_row de la classe cls pour créer une instance de la classe à l'aide des données de la ligne. L'instance est ensuite ajoutée à la liste records, et la fonction retourne la liste d'instances.

Dans ce laboratoire, nous allons refactoriser ces fonctions pour les rendre plus flexibles et robustes. Nous allons également explorer le système d'indication de type (type hinting) de Python, qui nous permet de spécifier les types attendus des paramètres et des valeurs de retour de nos fonctions. Cela peut rendre notre code plus lisible et plus facile à comprendre, en particulier pour les autres développeurs qui pourraient travailler avec notre code.

Commençons par créer un fichier reader.py et y ajouter ces fonctions initiales. Assurez - vous de tester ces fonctions pour vous assurer qu'elles fonctionnent correctement avant de passer aux étapes suivantes.

Création des fonctions de base pour la lecture de fichiers CSV

Commençons par créer un fichier reader.py avec deux fonctions de base pour lire des données CSV. Ces fonctions nous aideront à manipuler les fichiers CSV de différentes manières, comme convertir les données en dictionnaires ou en instances de classe.

Tout d'abord, nous devons comprendre ce qu'est un fichier CSV. CSV signifie Comma-Separated Values (Valeurs séparées par des virgules). C'est un format de fichier simple utilisé pour stocker des données tabulaires, où chaque ligne représente une ligne du tableau et les valeurs de chaque ligne sont séparées par des virgules.

Maintenant, créons le fichier reader.py. Suivez ces étapes :

  1. Ouvrez l'éditeur de code et créez un nouveau fichier appelé reader.py dans le répertoire /home/labex/project. C'est là que nous allons écrire nos fonctions pour lire les données CSV.

  2. Ajoutez le code suivant à reader.py :

## reader.py

import csv

def read_csv_as_dicts(filename, types):
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column

    Returns:
        List of dictionaries with data from the CSV file
    '''
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = { name: func(val)
                       for name, func, val in zip(headers, types, row) }
            records.append(record)
    return records

def read_csv_as_instances(filename, cls):
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from

    Returns:
        List of class instances with data from the CSV file
    '''
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = cls.from_row(row)
            records.append(record)
    return records

Dans la fonction read_csv_as_dicts, nous ouvrons d'abord le fichier CSV à l'aide de la fonction open. Ensuite, nous utilisons csv.reader pour lire le fichier ligne par ligne. L'instruction next(rows) lit la première ligne du fichier, qui contient généralement les en-têtes. Après cela, nous itérons sur les lignes restantes. Pour chaque ligne, nous créons un dictionnaire où les clés sont les en-têtes et les valeurs sont les valeurs correspondantes de la ligne, avec une conversion de type facultative à l'aide de la liste types.

La fonction read_csv_as_instances est similaire, mais au lieu de créer des dictionnaires, elle crée des instances d'une classe donnée. Elle suppose que la classe a une méthode statique appelée from_row qui peut créer une instance à partir d'une ligne de données.

  1. Testons ces fonctions pour nous assurer qu'elles fonctionnent correctement. Créez un nouveau fichier appelé test_reader.py avec le code suivant :
## test_reader.py

import reader
import stock

## Test reading CSV as dictionaries
portfolio_dicts = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("First portfolio item as dictionary:", portfolio_dicts[0])
print("Total items:", len(portfolio_dicts))

## Test reading CSV as class instances
portfolio_instances = reader.read_csv_as_instances('portfolio.csv', stock.Stock)
print("\nFirst portfolio item as Stock instance:", portfolio_instances[0])
print("Total items:", len(portfolio_instances))

Dans le fichier test_reader.py, nous importons le module reader que nous venons de créer et le module stock. Nous testons ensuite les deux fonctions en les appelant avec un fichier CSV d'exemple nommé portfolio.csv. Nous affichons le premier élément et le nombre total d'éléments du portefeuille pour vérifier que les fonctions fonctionnent comme prévu.

  1. Exécutez le script de test depuis le terminal :
python test_reader.py

La sortie devrait ressembler à ceci :

First portfolio item as dictionary: {'name': 'AA', 'shares': 100, 'price': 32.2}
Total items: 7

First portfolio item as Stock instance: Stock('AA', 100, 32.2)
Total items: 7

Cela confirme que nos deux fonctions fonctionnent correctement. La première fonction convertit les données CSV en une liste de dictionnaires avec une conversion de type appropriée, et la deuxième fonction crée des instances de classe à l'aide d'une méthode statique de la classe fournie.

Dans l'étape suivante, nous allons refactoriser ces fonctions pour les rendre plus flexibles en leur permettant de fonctionner avec n'importe quelle source de données itérable, pas seulement des noms de fichiers.

✨ Vérifier la solution et pratiquer

Rendre les fonctions plus flexibles

Actuellement, nos fonctions sont limitées à la lecture de fichiers spécifiés par un nom de fichier. Cela restreint leur utilisabilité. En programmation, il est souvent avantageux de rendre les fonctions plus flexibles afin qu'elles puissent gérer différents types d'entrée. Dans notre cas, il serait idéal que nos fonctions puissent fonctionner avec n'importe quel itérable qui produit des lignes, comme des objets de fichier ou d'autres sources. De cette façon, nous pouvons utiliser ces fonctions dans plus de scénarios, comme la lecture de fichiers compressés ou d'autres flux de données.

Refactorisons notre code pour permettre cette flexibilité :

  1. Ouvrez le fichier reader.py. Nous allons le modifier pour inclure de nouvelles fonctions. Ces nouvelles fonctions permettront à notre code de fonctionner avec différents types d'itérateurs. Voici le code à ajouter :
## reader.py

import csv

def csv_as_dicts(lines, types):
    '''
    Parse CSV data from an iterable into a list of dictionaries

    Args:
        lines: An iterable producing CSV lines
        types: List of type conversion functions for each column

    Returns:
        List of dictionaries with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)
    headers = next(rows)
    for row in rows:
        record = { name: func(val)
                  for name, func, val in zip(headers, types, row) }
        records.append(record)
    return records

def csv_as_instances(lines, cls):
    '''
    Parse CSV data from an iterable into a list of class instances

    Args:
        lines: An iterable producing CSV lines
        cls: Class to create instances from

    Returns:
        List of class instances with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)
    headers = next(rows)
    for row in rows:
        record = cls.from_row(row)
        records.append(record)
    return records

def read_csv_as_dicts(filename, types):
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column

    Returns:
        List of dictionaries with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_dicts(file, types)

def read_csv_as_instances(filename, cls):
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from

    Returns:
        List of class instances with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_instances(file, cls)

Examinons de plus près comment nous avons refactorisé le code :

  1. Nous avons créé deux fonctions plus génériques, csv_as_dicts() et csv_as_instances(). Ces fonctions sont conçues pour fonctionner avec n'importe quel itérable qui produit des lignes CSV. Cela signifie qu'elles peuvent gérer différents types de sources d'entrée, pas seulement des fichiers spécifiés par un nom de fichier.

  2. Nous avons réimplémenté read_csv_as_dicts() et read_csv_as_instances() pour utiliser ces nouvelles fonctions. De cette façon, la fonctionnalité originale de lecture à partir d'un fichier par nom de fichier est toujours disponible, mais maintenant elle est construite sur des fonctions plus flexibles.

  3. Cette approche maintient la compatibilité rétroactive avec le code existant. Cela signifie que tout code utilisant les anciennes fonctions continuera de fonctionner comme prévu. En même temps, notre bibliothèque devient plus flexible car elle peut maintenant gérer différents types de sources d'entrée.

  4. Maintenant, testons ces nouvelles fonctions. Créez un fichier appelé test_reader_flexibility.py et ajoutez le code suivant. Ce code testera les nouvelles fonctions avec différents types de sources d'entrée :

## test_reader_flexibility.py

import reader
import stock
import gzip

## Test opening a regular file
with open('portfolio.csv') as file:
    portfolio = reader.csv_as_dicts(file, [str, int, float])
    print("First item from open file:", portfolio[0])

## Test opening a gzipped file
with gzip.open('portfolio.csv.gz', 'rt') as file:  ## 'rt' means read text
    portfolio = reader.csv_as_instances(file, stock.Stock)
    print("\nFirst item from gzipped file:", portfolio[0])

## Test backward compatibility
portfolio = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("\nFirst item using backward compatible function:", portfolio[0])
  1. Après avoir créé le fichier de test, nous devons exécuter le script de test depuis le terminal. Ouvrez votre terminal et naviguez jusqu'au répertoire où se trouve le fichier test_reader_flexibility.py. Ensuite, exécutez la commande suivante :
python test_reader_flexibility.py

La sortie devrait ressembler à ceci :

First item from open file: {'name': 'AA', 'shares': 100, 'price': 32.2}

First item from gzipped file: Stock('AA', 100, 32.2)

First item using backward compatible function: {'name': 'AA', 'shares': 100, 'price': 32.2}

Cette sortie confirme que nos fonctions fonctionnent maintenant avec différents types de sources d'entrée tout en maintenant la compatibilité rétroactive. Les fonctions refactorisées peuvent traiter des données provenant de :

  • Des fichiers normaux ouverts avec open()
  • Des fichiers compressés ouverts avec gzip.open()
  • N'importe quel autre objet itérable qui produit des lignes de texte

Cela rend notre code beaucoup plus flexible et plus facile à utiliser dans différents scénarios.

✨ Vérifier la solution et pratiquer

Gestion des fichiers CSV sans en-têtes

Dans le domaine du traitement des données, tous les fichiers CSV ne comportent pas d'en-têtes dans leur première ligne. Les en-têtes sont les noms donnés à chaque colonne d'un fichier CSV, ce qui nous aide à comprendre le type de données que chaque colonne contient. Lorsqu'un fichier CSV n'a pas d'en-têtes, nous devons trouver un moyen de le gérer correctement. Dans cette section, nous allons modifier nos fonctions pour permettre à l'appelant de fournir manuellement les en-têtes, afin que nous puissions travailler avec des fichiers CSV avec et sans en-têtes.

  1. Ouvrez le fichier reader.py et mettez-le à jour pour inclure la gestion des en-têtes :
## reader.py

import csv

def csv_as_dicts(lines, types, headers=None):
    '''
    Parse CSV data from an iterable into a list of dictionaries

    Args:
        lines: An iterable producing CSV lines
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)

    if headers is None:
        ## Use the first row as headers if none provided
        headers = next(rows)

    for row in rows:
        record = { name: func(val)
                  for name, func, val in zip(headers, types, row) }
        records.append(record)
    return records

def csv_as_instances(lines, cls, headers=None):
    '''
    Parse CSV data from an iterable into a list of class instances

    Args:
        lines: An iterable producing CSV lines
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)

    if headers is None:
        ## Skip the first row if no headers provided
        next(rows)

    for row in rows:
        record = cls.from_row(row)
        records.append(record)
    return records

def read_csv_as_dicts(filename, types, headers=None):
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_dicts(file, types, headers)

def read_csv_as_instances(filename, cls, headers=None):
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_instances(file, cls, headers)

Comprenons les principales modifications que nous avons apportées à ces fonctions :

  1. Nous avons ajouté un paramètre headers à toutes les fonctions et nous l'avons défini par défaut sur None. Cela signifie que si l'appelant ne fournit pas d'en-têtes, les fonctions utiliseront le comportement par défaut.

  2. Dans la fonction csv_as_dicts, nous utilisons la première ligne comme en-têtes seulement si le paramètre headers est None. Cela nous permet de gérer automatiquement les fichiers avec en-têtes.

  3. Dans la fonction csv_as_instances, nous sautons la première ligne seulement si le paramètre headers est None. En effet, si nous fournissons nos propres en-têtes, la première ligne du fichier contient des données réelles, pas des en-têtes.

  4. Testons ces modifications avec notre fichier sans en-têtes. Créez un fichier appelé test_headers.py :

## test_headers.py

import reader
import stock

## Define column names for the file without headers
column_names = ['name', 'shares', 'price']

## Test reading a file without headers
portfolio = reader.read_csv_as_dicts('portfolio_noheader.csv',
                                     [str, int, float],
                                     headers=column_names)
print("First item from file without headers:", portfolio[0])
print("Total items:", len(portfolio))

## Test reading the same file as instances
portfolio = reader.read_csv_as_instances('portfolio_noheader.csv',
                                        stock.Stock,
                                        headers=column_names)
print("\nFirst item as Stock instance:", portfolio[0])
print("Total items:", len(portfolio))

## Verify that original functionality still works
portfolio = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("\nFirst item from file with headers:", portfolio[0])

Dans ce script de test, nous définissons d'abord les noms de colonne pour le fichier sans en-têtes. Ensuite, nous testons la lecture du fichier sans en-têtes sous forme de liste de dictionnaires et de liste d'instances de classe. Enfin, nous vérifions que la fonctionnalité originale fonctionne toujours en lisant un fichier avec en-têtes.

  1. Exécutez le script de test depuis le terminal :
python test_headers.py

La sortie devrait ressembler à :

First item from file without headers: {'name': 'AA', 'shares': 100, 'price': 32.2}
Total items: 7

First item as Stock instance: Stock('AA', 100, 32.2)
Total items: 7

First item from file with headers: {'name': 'AA', 'shares': 100, 'price': 32.2}

Cette sortie confirme que nos fonctions peuvent maintenant gérer les fichiers CSV avec et sans en-têtes. L'utilisateur peut fournir les noms de colonne si nécessaire, ou s'appuyer sur le comportement par défaut de lecture des en-têtes à partir de la première ligne.

En effectuant cette modification, nos fonctions de lecture de fichiers CSV sont maintenant plus polyvalentes et peuvent gérer une plus large gamme de formats de fichiers. C'est une étape importante pour rendre notre code plus robuste et utile dans différents scénarios.

Ajout d'indications de type

Dans Python 3.5 et les versions ultérieures, les indications de type (type hints) sont prises en charge. Les indications de type sont un moyen d'indiquer les types de données attendus pour les variables, les paramètres de fonction et les valeurs de retour dans votre code. Elles ne changent pas le fonctionnement du code, mais elles le rendent plus lisible et peuvent aider à détecter certains types d'erreurs avant l'exécution effective du code. Maintenant, ajoutons des indications de type à nos fonctions de lecture de fichiers CSV.

  1. Ouvrez le fichier reader.py et mettez-le à jour pour inclure les indications de type :
## reader.py

import csv
from typing import List, Callable, Dict, Any, Type, Optional, TextIO, Iterator, TypeVar

## Define a generic type for the class parameter
T = TypeVar('T')

def csv_as_dicts(lines: Iterator[str],
                types: List[Callable[[str], Any]],
                headers: Optional[List[str]] = None) -> List[Dict[str, Any]]:
    '''
    Parse CSV data from an iterable into a list of dictionaries

    Args:
        lines: An iterable producing CSV lines
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV lines
    '''
    records: List[Dict[str, Any]] = []
    rows = csv.reader(lines)

    if headers is None:
        ## Use the first row as headers if none provided
        headers = next(rows)

    for row in rows:
        record = { name: func(val)
                  for name, func, val in zip(headers, types, row) }
        records.append(record)
    return records

def csv_as_instances(lines: Iterator[str],
                    cls: Type[T],
                    headers: Optional[List[str]] = None) -> List[T]:
    '''
    Parse CSV data from an iterable into a list of class instances

    Args:
        lines: An iterable producing CSV lines
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV lines
    '''
    records: List[T] = []
    rows = csv.reader(lines)

    if headers is None:
        ## Skip the first row if no headers provided
        next(rows)

    for row in rows:
        record = cls.from_row(row)
        records.append(record)
    return records

def read_csv_as_dicts(filename: str,
                     types: List[Callable[[str], Any]],
                     headers: Optional[List[str]] = None) -> List[Dict[str, Any]]:
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_dicts(file, types, headers)

def read_csv_as_instances(filename: str,
                         cls: Type[T],
                         headers: Optional[List[str]] = None) -> List[T]:
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_instances(file, cls, headers)

Comprenons les principales modifications que nous avons apportées au code :

  1. Nous avons importé des types du module typing. Ce module fournit un ensemble de types que nous pouvons utiliser pour définir des indications de type. Par exemple, List, Dict et Optional sont des types de ce module.

  2. Nous avons ajouté une variable de type générique T pour représenter le type de classe. Une variable de type générique nous permet d'écrire des fonctions qui peuvent fonctionner avec différents types de manière sûre en termes de types.

  3. Nous avons ajouté des indications de type à tous les paramètres de fonction et aux valeurs de retour. Cela rend clair quels types d'arguments une fonction attend et quel type de valeur elle retourne.

  4. Nous avons utilisé des types de conteneurs appropriés tels que List, Dict et Optional. List représente une liste, Dict représente un dictionnaire et Optional indique qu'un paramètre peut avoir un certain type ou être None.

  5. Nous avons utilisé Callable pour les fonctions de conversion de type. Callable est utilisé pour indiquer qu'un paramètre est une fonction qui peut être appelée.

  6. Nous avons utilisé le type générique T pour indiquer que csv_as_instances retourne une liste d'instances de la classe passée en paramètre. Cela aide l'IDE et d'autres outils à comprendre le type des objets retournés.

  7. Maintenant, créons un simple fichier de test pour nous assurer que tout fonctionne toujours correctement :

## test_types.py

import reader
import stock

## The functions should work exactly as before
portfolio = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("First item:", portfolio[0])

## But now we have better type checking and IDE support
stock_portfolio = reader.read_csv_as_instances('portfolio.csv', stock.Stock)
print("\nFirst stock:", stock_portfolio[0])

## We can see that stock_portfolio is a list of Stock objects
## This helps IDEs provide better code completion
first_stock = stock_portfolio[0]
print(f"\nName: {first_stock.name}")
print(f"Shares: {first_stock.shares}")
print(f"Price: {first_stock.price}")
print(f"Value: {first_stock.shares * first_stock.price}")
  1. Exécutez le script de test depuis le terminal :
python test_types.py

La sortie devrait ressembler à :

First item: {'name': 'AA', 'shares': 100, 'price': 32.2}

First stock: Stock('AA', 100, 32.2)

Name: AA
Shares: 100
Price: 32.2
Value: 3220.0

Les indications de type ne changent pas le fonctionnement du code, mais elles offrent plusieurs avantages :

  1. Elles offrent un meilleur support de l'IDE avec l'autocomplétion du code. Lorsque vous utilisez un IDE comme PyCharm ou VS Code, il peut utiliser les indications de type pour suggérer les méthodes et les attributs corrects pour vos variables.
  2. Elles fournissent une documentation plus claire sur les types de paramètres et de valeurs de retour attendus. En regardant simplement la définition de la fonction, vous pouvez savoir quels types d'arguments elle attend et quel type de valeur elle retourne.
  3. Elles vous permettent d'utiliser des vérificateurs de types statiques comme mypy pour détecter les erreurs tôt. Les vérificateurs de types statiques analysent votre code sans l'exécuter et peuvent trouver des erreurs liées aux types avant d'exécuter le code.
  4. Elles améliorent la lisibilité et la maintenabilité du code. Lorsque vous ou d'autres développeurs reviendrez sur le code plus tard, il sera plus facile de comprendre ce que le code fait.

Dans une base de code importante, ces avantages peuvent réduire considérablement les bugs et rendre le code plus facile à comprendre et à maintenir.

Remarque : Les indications de type sont facultatives en Python, mais elles sont de plus en plus utilisées dans le code professionnel. Des bibliothèques telles que celles de la bibliothèque standard de Python et de nombreux packages tiers populaires incluent désormais de nombreuses indications de type.

Résumé

Dans ce laboratoire, vous avez appris plusieurs aspects clés de la conception de fonctions en Python. Tout d'abord, vous avez appris la conception de fonctions de base, en particulier comment écrire des fonctions pour traiter des données CSV dans diverses structures de données. Vous avez également exploré la flexibilité des fonctions en refactorisant des fonctions pour qu'elles fonctionnent avec n'importe quelle source itérable, améliorant ainsi la polyvalence et la réutilisabilité du code.

De plus, vous avez maîtrisé l'ajout de paramètres optionnels pour gérer différents cas d'utilisation, comme les fichiers CSV avec ou sans en-têtes, et l'utilisation du système d'indication de type (type hinting) de Python pour améliorer la lisibilité et la maintenabilité du code. Ces compétences sont essentielles pour écrire un code Python robuste, et à mesure que vos programmes deviendront plus complexes, ces principes de conception permettront de garder votre code organisé et compréhensible. Ces techniques peuvent être appliquées au-delà du traitement des CSV, ce qui les rend précieuses dans votre boîte à outils de programmation Python.