Aller au contenu

Introduction aux types Python

🌐 Traduction par IA et humains

Cette traduction a été réalisée par une IA guidée par des humains. 🤝

Elle peut contenir des erreurs d'interprétation du sens original, ou paraître peu naturelle, etc. 🤖

Vous pouvez améliorer cette traduction en nous aidant à mieux guider le LLM d'IA.

Version anglaise

Python prend en charge des « annotations de type » (aussi appelées « type hints ») facultatives.

Ces « annotations de type » sont une syntaxe spéciale qui permet de déclarer le type d'une variable.

En déclarant les types de vos variables, les éditeurs et outils peuvent vous offrir un meilleur support.

Ceci est un tutoriel rapide / rappel à propos des annotations de type Python. Il couvre uniquement le minimum nécessaire pour les utiliser avec FastAPI ... ce qui est en réalité très peu.

FastAPI est totalement basé sur ces annotations de type, elles lui donnent de nombreux avantages et bénéfices.

Mais même si vous n'utilisez jamais FastAPI, vous auriez intérêt à en apprendre un peu à leur sujet.

Remarque

Si vous êtes un expert Python, et que vous savez déjà tout sur les annotations de type, passez au chapitre suivant.

Motivation

Commençons par un exemple simple :

def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Exécuter ce programme affiche :

John Doe

La fonction fait ce qui suit :

  • Prend un first_name et un last_name.
  • Convertit la première lettre de chacun en majuscule avec title().
  • Concatène ces deux valeurs avec un espace au milieu.
def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Modifier le code

C'est un programme très simple.

Mais maintenant imaginez que vous l'écriviez de zéro.

À un certain moment, vous auriez commencé la définition de la fonction, vous aviez les paramètres prêts ...

Mais ensuite vous devez appeler « cette méthode qui convertit la première lettre en majuscule ».

Était-ce upper ? Était-ce uppercase ? first_uppercase ? capitalize ?

Vous essayez alors avec l'ami de toujours des programmeurs, l'autocomplétion de l'éditeur.

Vous tapez le premier paramètre de la fonction, first_name, puis un point (.) et appuyez sur Ctrl+Espace pour déclencher l'autocomplétion.

Mais, malheureusement, vous n'obtenez rien d'utile :

Ajouter des types

Modifions une seule ligne de la version précédente.

Nous allons changer exactement ce fragment, les paramètres de la fonction, de :

    first_name, last_name

à :

    first_name: str, last_name: str

C'est tout.

Ce sont les « annotations de type » :

def get_full_name(first_name: str, last_name: str):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Ce n'est pas la même chose que de déclarer des valeurs par défaut, ce qui serait :

    first_name="john", last_name="doe"

C'est différent.

Nous utilisons des deux-points (:), pas des signes égal (=).

Et ajouter des annotations de type ne change normalement pas ce qui se passe par rapport à ce qui se passerait sans elles.

Mais maintenant, imaginez que vous êtes à nouveau en train de créer cette fonction, mais avec des annotations de type.

Au même moment, vous essayez de déclencher l'autocomplétion avec Ctrl+Espace et vous voyez :

Avec cela, vous pouvez faire défiler en voyant les options, jusqu'à trouver celle qui « vous dit quelque chose » :

Plus de motivation

Regardez cette fonction, elle a déjà des annotations de type :

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + age
    return name_with_age

Comme l'éditeur connaît les types des variables, vous n'obtenez pas seulement l'autocomplétion, vous obtenez aussi des vérifications d'erreurs :

Vous savez maintenant qu'il faut corriger, convertir age en chaîne avec str(age) :

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + str(age)
    return name_with_age

Déclarer des types

Vous venez de voir l'endroit principal pour déclarer des annotations de type : dans les paramètres des fonctions.

C'est aussi l'endroit principal où vous les utiliserez avec FastAPI.

Types simples

Vous pouvez déclarer tous les types standards de Python, pas seulement str.

Vous pouvez utiliser, par exemple :

  • int
  • float
  • bool
  • bytes
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
    return item_a, item_b, item_c, item_d, item_e
🤓 Other versions and variants
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
    return item_a, item_b, item_c, item_d, item_e

Module typing

Pour certains cas d'utilisation supplémentaires, vous pourriez avoir besoin d'importer certains éléments depuis le module standard typing, par exemple lorsque vous voulez déclarer que quelque chose a « n'importe quel type », vous pouvez utiliser Any depuis typing :

from typing import Any


def some_function(data: Any):
    print(data)

Types génériques

Certains types peuvent prendre des « paramètres de type » entre crochets, pour définir leurs types internes, par exemple une « liste de chaînes » se déclarerait list[str].

Ces types qui peuvent prendre des paramètres de type sont appelés des types génériques ou Generics.

Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :

  • list
  • tuple
  • set
  • dict

Liste

Par exemple, définissons une variable comme une list de str.

Déclarez la variable, en utilisant la même syntaxe avec deux-points (:).

Comme type, mettez list.

Comme la liste est un type qui contient des types internes, mettez-les entre crochets :

def process_items(items: list[str]):
    for item in items:
        print(item)

Info

Ces types internes entre crochets sont appelés « paramètres de type ».

Dans ce cas, str est le paramètre de type passé à list.

Cela signifie : « la variable items est une list, et chacun des éléments de cette liste est un str ».

En faisant cela, votre éditeur peut vous fournir de l'aide même pendant le traitement des éléments de la liste :

Sans types, c'est presque impossible à réaliser.

Remarquez que la variable item est l'un des éléments de la liste items.

Et pourtant, l'éditeur sait que c'est un str et fournit le support approprié.

Tuple et Set

Vous feriez la même chose pour déclarer des tuple et des set :

def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
    return items_t, items_s

Cela signifie :

  • La variable items_t est un tuple avec 3 éléments, un int, un autre int, et un str.
  • La variable items_s est un set, et chacun de ses éléments est de type bytes.

Dict

Pour définir un dict, vous passez 2 paramètres de type, séparés par des virgules.

Le premier paramètre de type est pour les clés du dict.

Le second paramètre de type est pour les valeurs du dict :

def process_items(prices: dict[str, float]):
    for item_name, item_price in prices.items():
        print(item_name)
        print(item_price)

Cela signifie :

  • La variable prices est un dict :
    • Les clés de ce dict sont de type str (disons, le nom de chaque article).
    • Les valeurs de ce dict sont de type float (disons, le prix de chaque article).

Union

Vous pouvez déclarer qu'une variable peut être plusieurs types, par exemple, un int ou un str.

Pour le définir, vous utilisez la barre verticale (|) pour séparer les deux types.

C'est ce qu'on appelle une « union », car la variable peut être n'importe quoi dans l'union de ces deux ensembles de types.

def process_item(item: int | str):
    print(item)

Cela signifie que item peut être un int ou un str.

Possiblement None

Vous pouvez déclarer qu'une valeur peut avoir un type, comme str, mais qu'elle peut aussi être None.

def say_hi(name: str | None = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

Utiliser str | None au lieu de simplement str permettra à l'éditeur de vous aider à détecter des erreurs où vous supposeriez qu'une valeur est toujours un str, alors qu'elle pourrait en fait aussi être None.

Classes en tant que types

Vous pouvez aussi déclarer une classe comme type d'une variable.

Disons que vous avez une classe Person, avec un nom :

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

Vous pouvez ensuite déclarer une variable de type Person :

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

Et là encore, vous obtenez tout le support de l'éditeur :

Remarquez que cela signifie « one_person est une instance de la classe Person ».

Cela ne signifie pas « one_person est la classe appelée Person ».

Modèles Pydantic

Pydantic est une bibliothèque Python pour effectuer de la validation de données.

Vous déclarez la « forme » de la donnée sous forme de classes avec des attributs.

Et chaque attribut a un type.

Ensuite, vous créez une instance de cette classe avec certaines valeurs et elle validera les valeurs, les convertira dans le type approprié (le cas échéant) et vous donnera un objet avec toutes les données.

Et vous obtenez tout le support de l'éditeur avec cet objet résultant.

Un exemple tiré de la documentation officielle de Pydantic :

from datetime import datetime

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str = "John Doe"
    signup_ts: datetime | None = None
    friends: list[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123

Info

Pour en savoir plus à propos de Pydantic, consultez sa documentation.

FastAPI est entièrement basé sur Pydantic.

Vous verrez beaucoup plus de tout cela en pratique dans le Tutoriel - Guide utilisateur.

Annotations de type avec métadonnées

Python dispose également d'une fonctionnalité qui permet de mettre des métadonnées supplémentaires dans ces annotations de type en utilisant Annotated.

Vous pouvez importer Annotated depuis typing.

from typing import Annotated


def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
    return f"Hello {name}"

Python lui-même ne fait rien avec ce Annotated. Et pour les éditeurs et autres outils, le type est toujours str.

Mais vous pouvez utiliser cet espace dans Annotated pour fournir à FastAPI des métadonnées supplémentaires sur la façon dont vous voulez que votre application se comporte.

L'important à retenir est que le premier « paramètre de type » que vous passez à Annotated est le type réel. Le reste n'est que des métadonnées pour d'autres outils.

Pour l'instant, vous avez juste besoin de savoir que Annotated existe, et que c'est du Python standard. 😎

Plus tard, vous verrez à quel point cela peut être puissant.

Astuce

Le fait que ce soit du Python standard signifie que vous bénéficierez toujours de la meilleure expérience développeur possible dans votre éditeur, avec les outils que vous utilisez pour analyser et refactoriser votre code, etc. ✨

Et aussi que votre code sera très compatible avec de nombreux autres outils et bibliothèques Python. 🚀

Annotations de type dans FastAPI

FastAPI tire parti de ces annotations de type pour faire plusieurs choses.

Avec FastAPI, vous déclarez des paramètres avec des annotations de type et vous obtenez :

  • Du support de l'éditeur.
  • Des vérifications de types.

... et FastAPI utilise les mêmes déclarations pour :

  • Définir des prérequis : à partir des paramètres de chemin de la requête, des paramètres de requête, des en-têtes, des corps, des dépendances, etc.
  • Convertir des données : de la requête vers le type requis.
  • Valider des données : provenant de chaque requête :
    • En générant des erreurs automatiques renvoyées au client lorsque la donnée est invalide.
  • Documenter l'API avec OpenAPI :
    • ce qui est ensuite utilisé par les interfaces utilisateur de documentation interactive automatiques.

Tout cela peut sembler abstrait. Ne vous inquiétez pas. Vous verrez tout cela en action dans le Tutoriel - Guide utilisateur.

L'important est qu'en utilisant les types standards de Python, en un seul endroit (au lieu d'ajouter plus de classes, de décorateurs, etc.), FastAPI fera une grande partie du travail pour vous.

Info

Si vous avez déjà parcouru tout le tutoriel et êtes revenu pour en voir plus sur les types, une bonne ressource est l'« aide-mémoire » de mypy.