Python

Pourquoi la programmation orientée objet en Python reste-t-elle si utile ?

La programmation orientée objet en Python traîne parfois une drôle de réputation. On la présente soit comme un passage obligé pour faire du code « sérieux », soit comme une vieille habitude dont on pourrait se passer. La réalité est plus simple. En Python, l’objet n’est pas un décor académique. C’est une manière très pratique d’organiser un programme quand plusieurs morceaux de logique doivent vivre ensemble sans se marcher dessus.

C’est aussi ce qui rend le langage agréable à lire. Quand une classe est bien pensée, on comprend vite ce qu’elle sait faire, ce qu’elle protège, et comment elle dialogue avec le reste du code. Si vous avez déjà apprécié un article sur les bases du langage ou la façon de structurer un script, vous pouvez prolonger la lecture avec ce billet puis avec cet autre article, qui donnent un bon terrain pour replacer l’objet dans un ensemble plus large.

Une classe ne sert pas seulement à ranger des données

C’est sans doute le malentendu le plus fréquent. Beaucoup de débutants écrivent une classe comme une simple boîte avec trois attributs, puis se demandent à quoi bon. En Python, une classe devient intéressante quand elle porte un comportement. Un objet Panier ne se contente pas de stocker une liste d’articles. Il sait ajouter un produit, calculer un total, vérifier une remise, refuser une quantité absurde, et exposer une représentation lisible pour le reste du programme.

Autrement dit, l’objet rassemble ce qui va ensemble. Les données d’un côté, les règles de l’autre, et surtout la promesse que ces règles seront appliquées au bon endroit. C’est là que l’OOP devient utile en Python. Pas pour faire compliqué, mais pour éviter les fonctions dispersées qui manipulent n’importe comment des dictionnaires anonymes.

Cette idée change aussi la lecture du code. Quand on voit facture.total_ttc(), on comprend immédiatement où regarder. Quand la logique est répartie dans une demi-douzaine de fonctions qui reçoivent toutes le même dictionnaire, il faut remonter le fil à la main. L’approche objet ne rend pas automatiquement un projet propre, mais elle donne une grammaire claire pour le rendre lisible.

Les méthodes spéciales montrent la vraie personnalité de Python

from enum import Enum

class Size(Enum):
    SMALL = 1
    LARGE = 2

print(Size.SMALL.value)

Une bonne entrée dans l’OOP version Python, ce sont les méthodes spéciales. Elles rappellent qu’un objet Python n’est pas seulement une structure de stockage. Il peut réagir à des opérations du langage lui-même. Avec __repr__, un objet devient plus simple à inspecter. Avec __len__, il peut annoncer sa taille. Avec __iter__, il devient parcourable. Avec __bool__, il peut dire dans quel cas il doit être considéré comme vrai ou faux.

On comprend alors quelque chose d’important : écrire une classe, ce n’est pas inventer un mini-format privé. C’est brancher un comportement sur les conventions déjà connues du langage. Un objet bien écrit se manipule presque comme un type natif. Il s’intègre à print(), aux boucles, aux comparaisons, aux tests, parfois même au tri.

C’est pour cette raison que l’OOP en Python n’a rien de figé. On ne construit pas des hiérarchies pour le plaisir. On cherche plutôt à fabriquer des objets qui jouent proprement avec l’écosystème Python. Quand cette intégration est réussie, le code gagne en souplesse sans devenir opaque.

Enums, surcharge d’opérateurs et objets qui parlent le langage

class Price:
    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return Price(self.amount + other.amount)

Le brouillon centré sur les enums, la surcharge d’opérateurs et un peu d’introspection partait dans une bonne direction, parce que ces trois points montrent justement qu’une classe peut faire plus que contenir des champs.

Les Enum apportent d’abord de la clarté. Au lieu de faire circuler des chaînes comme "draft", "published" ou "archived" un peu partout, on crée un vocabulaire stable. Un état devient une valeur explicite, facile à comparer et plus difficile à casser par une faute de frappe. C’est discret, mais très efficace dans un vrai projet.

La surcharge d’opérateurs va plus loin. Elle peut sembler gadget au début, alors qu’elle est surtout utile quand elle colle au sens métier. Si vous avez une classe Vecteur, additionner deux instances avec + est naturel. Si vous avez une classe Montant, comparer deux valeurs avec < ou additionner des sommes peut rendre le code plus direct. L’important est de rester honnête. Un opérateur surchargé doit faire ce qu’on attend de lui, sans surprise.

Les méthodes comme __add__, __eq__ ou __lt__ ne sont donc pas là pour faire joli. Elles servent à écrire des objets qui comprennent les gestes ordinaires du langage. Et quand on relit le code quelques semaines plus tard, cette cohérence compte énormément.

Lire du code orienté objet, c’est suivre des responsabilités

Il y a aussi une façon très concrète d’aborder l’OOP : apprendre à lire les classes. En Python, cela revient souvent à repérer trois choses. Quels attributs décrivent l’état de l’objet ? Quelles méthodes modifient cet état ? Quelles méthodes exposent une intention claire au reste du programme ?

L’introspection aide beaucoup à cette étape. Sans entrer dans une démonstration trop lourde, des outils comme type(), isinstance(), dir() ou help() permettent de comprendre rapidement à quoi on a affaire. C’est particulièrement utile quand on découvre une bibliothèque ou un vieux module écrit par quelqu’un d’autre. On ne lit plus seulement du texte, on interroge les objets eux-mêmes pour voir comment ils se présentent.

Cette habitude change la manière d’apprendre Python. Au lieu de mémoriser des recettes isolées, on observe comment les objets se comportent, quels contrats ils proposent, et pourquoi certaines API semblent immédiatement naturelles. L’OOP devient alors moins théorique. Elle ressemble davantage à une lecture attentive du langage en train de se déployer dans le code.

Au fond, ce qui rend la programmation orientée objet intéressante en Python, ce n’est pas la promesse d’un modèle parfait. C’est sa capacité à rapprocher la structure du code et la façon dont on pense un problème concret. Une classe utile stocke un peu, protège beaucoup, et explique par son interface comment elle veut être utilisée.

Gestionnaire de paquet Python 4/4

python -m pip download package -d downloads/

Télécharger uniquement une wheel :

python -m pip download --only-binary=:all: package -d downloads/

Afficher les informations :

python -m pip index versions package

Installer avec davantage de détails :

python -m pip install -vvv package

68. Hashes

Un fichier de dépendances peut inclure des hashes :

requests==2.32.3 \
    --hash=sha256:...

Installation stricte :

python -m pip install --require-hashes -r requirements.txt

Cela permet de vérifier que les fichiers téléchargés correspondent exactement aux artefacts attendus.

Partie 20 — Workflow professionnel recommandé

69. Workflow minimal avec venv et pip

Windows

mkdir mon-projet
cd mon-projet

py -3.12 -m venv .venv
.venv\Scripts\Activate.ps1

python -m pip install --upgrade pip
python -m pip install fastapi uvicorn

python -m pip freeze > requirements.txt

Pour recréer :

py -3.12 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt

70. Workflow recommandé avec uv

uv init mon-projet
cd mon-projet

uv python pin 3.12
uv add fastapi uvicorn
uv add --dev pytest ruff

uv run pytest
uv run uvicorn main:app --reload

Fichiers à versionner :

pyproject.toml
uv.lock
.python-version
src/
tests/

À ignorer :

.venv/
__pycache__/
.pytest_cache/
.ruff_cache/

71. Exemple de projet complet

mon-api/
├── .gitignore
├── .python-version
├── pyproject.toml
├── uv.lock
├── src/
│   └── mon_api/
│       ├── __init__.py
│       └── main.py
└── tests/
    └── test_main.py

pyproject.toml :

[project]
name = "mon-api"
version = "0.1.0"
requires-python = ">=3.12,<3.13"

dependencies = [
    "fastapi>=0.115,<1",
    "uvicorn[standard]>=0.34,<1",
]

[dependency-groups]
dev = [
    "pytest>=8,<9",
    "ruff>=0.12,<1",
]

Commandes :

uv sync
uv run uvicorn mon_api.main:app --reload
uv run pytest
uv run ruff check .

Partie 21 — Procédure universelle de diagnostic

72. Étape 1 : identifier le Python

python --version
python -c "import sys; print(sys.executable)"

Sous Windows :

py --list
where python

Sous Linux :

type -a python
type -a python3

73. Étape 2 : identifier pip

python -m pip --version

Comparer son chemin avec :

python -c "import sys; print(sys.executable)"

74. Étape 3 : identifier l’environnement

python -c "import sys; print('prefix:', sys.prefix); print('base:', sys.base_prefix); print('venv:', sys.prefix != sys.base_prefix)"

75. Étape 4 : vérifier la compatibilité

python -m pip index versions nom-du-paquet
python -m pip install -vvv nom-du-paquet

Lire notamment :

  • Requires-Python ;
  • les versions candidates ignorées ;
  • les wheels rejetées ;
  • les conflits de dépendances.

76. Étape 5 : tester la disponibilité d’une wheel

python -m pip install --only-binary=:all: nom-du-paquet

Si cela échoue mais que l’installation normale tente de compiler, aucune wheel compatible n’est probablement disponible.

77. Étape 6 : inspecter les tags

python -m pip debug --verbose

Comparer avec le fichier téléchargé :

cp312-cp312-win_amd64

78. Étape 7 : repartir proprement

deactivate

Supprimer .venv, puis :

python-version-voulue -m venv .venv

Réinstaller depuis le fichier de dépendances.

Partie 22 — Les règles à retenir

79. Les dix règles essentielles

Règle 1

Un environnement virtuel ne télécharge pas automatiquement une autre version de Python.

Il utilise le Python qui le crée.

Règle 2

Toujours préférer :

python -m pip

à :

pip

Règle 3

Un environnement virtuel par projet.

Règle 4

Ne pas installer les dépendances de projet dans le Python global.

Règle 5

Ne pas utiliser sudo pip install.

Règle 6

Une wheel doit correspondre :

  • à l’implémentation Python ;
  • à la version Python ;
  • à l’ABI ;
  • au système ;
  • à l’architecture.

Règle 7

Une compilation déclenchée par pip signifie souvent qu’aucune wheel compatible n’a été trouvée.

Règle 8

Ne pas copier .venv entre machines.

Le recréer à partir des métadonnées.

Règle 9

Versionner :

pyproject.toml
fichier de lock
.python-version

mais pas :

.venv

Règle 10

Pour un nouveau projet, uv constitue aujourd’hui une solution très cohérente pour gérer simultanément Python, les environnements, les dépendances et le verrouillage.

Partie 23 — Arbre de décision rapide

Je veux commencer un projet

Ai-je une version Python imposée ?
│
├── Oui
│   └── Installer ou sélectionner cette version
│
└── Non
    └── Choisir une version stable supportée par les bibliothèques

Puis :

Projet moderne ?
│
├── Oui → uv + pyproject.toml + uv.lock
│
└── Non → venv + pip + requirements.txt

Une installation échoue

L’erreur parle de Requires-Python ?
│
├── Oui → mauvaise version de Python
│
└── Non
    │
    ├── ResolutionImpossible ?
    │   └── conflit de versions
    │
    ├── Failed building wheel ?
    │   └── compilation native ou absence de wheel
    │
    ├── unsupported wheel ?
    │   └── mauvais Python, OS ou architecture
    │
    └── ModuleNotFoundError après installation ?
        └── pip et python ne visent pas le même environnement

Commande de diagnostic finale

Sous Windows PowerShell :

Write-Host "=== Python ==="
python --version
python -c "import sys, platform; print('Executable:', sys.executable); print('Prefix:', sys.prefix); print('Base prefix:', sys.base_prefix); print('Virtual env:', sys.prefix != sys.base_prefix); print('Implementation:', platform.python_implementation()); print('Machine:', platform.machine())"

Write-Host "`n=== pip ==="
python -m pip --version

Write-Host "`n=== Paths ==="
where.exe python
where.exe pip

Write-Host "`n=== Packages ==="
python -m pip list

Sous Linux ou macOS :

echo "=== Python ==="
python --version
python -c "import sys, platform; print('Executable:', sys.executable); print('Prefix:', sys.prefix); print('Base prefix:', sys.base_prefix); print('Virtual env:', sys.prefix != sys.base_prefix); print('Implementation:', platform.python_implementation()); print('Machine:', platform.machine())"

echo
echo "=== pip ==="
python -m pip --version

echo
echo "=== Paths ==="
type -a python
type -a pip

echo
echo "=== Packages ==="
python -m pip list

La sortie de ces commandes permet de résoudre la majorité des problèmes de gestion de paquets Python.

Gestionnaire de paquet Python 3/4

Partie 10 — pyproject.toml

38. Le fichier central des projets modernes

Exemple :

[project]
name = "mon-projet"
version = "0.1.0"
description = "Exemple"
requires-python = ">=3.12,<3.13"

dependencies = [
    "fastapi>=0.115,<1",
    "sqlalchemy>=2,<3",
    "psycopg[binary]>=3.2,<4",
]

[project.optional-dependencies]
dev = [
    "pytest>=8",
    "ruff>=0.12",
    "mypy>=1.16",
]

Installation du projet :

python -m pip install .

Installation en mode éditable :

python -m pip install -e .

Avec les dépendances de développement :

python -m pip install -e ".[dev]"

39. Mode éditable

Avec :

python -m pip install -e .

le projet local est installé de manière à ce que les changements de code soient immédiatement visibles sans réinstallation complète.

C’est le mode habituel pour développer une bibliothèque ou une application structurée comme un paquet Python.

40. Build backend

Exemple utilisant setuptools :

[build-system]
requires = ["setuptools>=75", "wheel"]
build-backend = "setuptools.build_meta"

Exemple avec Hatchling :

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Exemple avec uv_build :

[build-system]
requires = ["uv_build"]
build-backend = "uv_build"

Le backend est l’outil chargé de construire la distribution du projet.

Partie 11 — Installer plusieurs versions de Python

41. Sous Windows : utiliser le lanceur py

Lister les versions :

py --list

Créer un environnement en Python 3.11 :

py -3.11 -m venv .venv

Créer un environnement en Python 3.12 :

py -3.12 -m venv .venv

Exécuter un script avec une version précise :

py -3.11 script.py

Installer un paquet dans le Python global 3.12 :

py -3.12 -m pip install package

Mais pour un projet, on préférera :

py -3.12 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install package

42. Sous Linux : ne pas remplacer le Python système

Éviter de modifier manuellement :

/usr/bin/python3

Éviter également de supprimer le Python installé par la distribution.

Les outils système peuvent dépendre d’une version précise.

Approches recommandées :

  • paquets officiels de la distribution ;
  • pyenv ;
  • uv ;
  • conteneurs Docker ;
  • compilation dans /opt ou dans le dossier utilisateur ;
  • environnements virtuels.

43. pyenv

pyenv permet de gérer plusieurs interpréteurs Python.

Commandes typiques :

pyenv install 3.11.11
pyenv install 3.12.10
pyenv versions
pyenv local 3.12.10

Le fichier :

.python-version

peut contenir :

3.12.10

pyenv sélectionne alors cette version dans le répertoire du projet.

Important : pyenv choisit l’interpréteur, mais ne gère pas à lui seul toutes les dépendances du projet.

On peut l’associer à venv :

pyenv local 3.12.10
python -m venv .venv

Partie 12 — uv, approche moderne recommandée

44. Qu’est-ce que uv ?

uv est un gestionnaire Python et de projets écrit en Rust. Il peut notamment :

  • installer plusieurs versions de Python ;
  • créer les environnements virtuels ;
  • résoudre les dépendances ;
  • installer les paquets ;
  • créer un fichier de verrouillage ;
  • lancer des commandes ;
  • gérer des outils CLI isolés.

La documentation officielle le présente comme un gestionnaire de paquets et de projets Python rapide.

45. Gérer Python avec uv

Lister les versions :

uv python list

Installer Python 3.12 :

uv python install 3.12

Trouver l’interpréteur :

uv python find 3.12

Fixer la version du projet :

uv python pin 3.12

Cela crée généralement :

.python-version

uv sait installer, lister, trouver, fixer et désinstaller des versions de Python.

46. Créer un projet avec uv

uv init mon-projet
cd mon-projet
uv python pin 3.12
uv add fastapi
uv add sqlalchemy
uv add --dev pytest ruff

Structure possible :

mon-projet/
├── .python-version
├── .venv/
├── pyproject.toml
├── uv.lock
└── src/

Le fichier .python-version indique la version Python par défaut du projet, tandis que .venv contient son environnement isolé.

47. Synchroniser l’environnement

uv sync

Cette commande crée ou met à jour .venv selon :

pyproject.toml
uv.lock

Lancer le projet :

uv run python main.py

Lancer les tests :

uv run pytest

Il n’est pas obligatoire d’activer manuellement .venv.

48. Ajouter et retirer des dépendances

uv add requests
uv add "django>=5,<6"
uv add --dev pytest
uv remove requests

uv met à jour :

  • pyproject.toml ;
  • uv.lock ;
  • l’environnement.

49. Pourquoi uv simplifie le problème

Avec la méthode historique, on doit coordonner :

installation Python
+ venv
+ pip
+ requirements.txt
+ éventuellement pip-tools
+ éventuellement pipx

Avec uv, on peut coordonner :

version Python
+ environnement
+ dépendances
+ lockfile
+ commandes

Workflow recommandé :

uv init
uv python pin 3.12
uv add fastapi
uv add --dev pytest
uv run pytest

Partie 13 — pipx et outils en ligne de commande

50. Pourquoi ne pas installer les CLI globalement ?

Des outils comme :

  • black ;
  • ruff ;
  • poetry ;
  • httpie ;
  • cookiecutter ;
  • ansible ;

sont des applications Python, pas forcément des dépendances d’un projet.

Les installer globalement peut provoquer des conflits.

pipx installe chaque outil dans son propre environnement virtuel tout en exposant sa commande dans le PATH.

Exemple :

pipx install poetry
pipx install httpie
pipx list
pipx upgrade-all

Avec uv :

uv tool install ruff
uv tool install httpie
uvx ruff check .

Chaque outil géré par uv est lié à une version précise de Python et dispose de son environnement isolé.

Partie 14 — Erreurs fréquentes et diagnostic

51. ModuleNotFoundError

Erreur :

ModuleNotFoundError: No module named 'requests'

Vérifications :

python -c "import sys; print(sys.executable)"
python -m pip show requests
python -m pip list

Puis :

python -m pip install requests

Ne pas se contenter de :

pip install requests

car ce pip peut appartenir à un autre Python.

52. pip n’est pas reconnu

Sous Windows :

py -m pip --version

Si cela fonctionne, utiliser :

py -m pip install package

Dans un environnement virtuel :

python -m pip install package

Si pip manque :

python -m ensurepip --upgrade

Puis :

python -m pip install --upgrade pip

53. Mauvaise version de Python

Erreur :

Package requires Python >=3.11

Diagnostic :

python --version
python -c "import sys; print(sys.executable)"

Solution Windows :

py -3.12 -m venv .venv

Solution Linux avec un Python déjà installé :

python3.12 -m venv .venv

Un environnement existant en Python 3.10 ne peut pas être transformé proprement en environnement Python 3.12.

Il faut le recréer.

54. No matching distribution found

Exemple :

ERROR: Could not find a version that satisfies the requirement package
ERROR: No matching distribution found

Causes possibles :

  • nom du paquet incorrect ;
  • version inexistante ;
  • Python trop ancien ;
  • Python trop récent ;
  • architecture non supportée ;
  • absence de wheel et source indisponible ;
  • index privé mal configuré ;
  • préversion ignorée ;
  • paquet retiré ;
  • contraintes incompatibles.

Diagnostic :

python --version
python -m pip index versions package
python -m pip install -vvv package

Pour autoriser une préversion :

python -m pip install --pre package

55. ResolutionImpossible

Lancer :

python -m pip install -vvv -r requirements.txt

Puis rechercher les contraintes contradictoires.

Exemple :

A demande urllib3<2
B demande urllib3>=2

Solutions possibles :

  • mettre à jour A ;
  • mettre à jour B ;
  • rétrograder l’un des paquets ;
  • remplacer une bibliothèque ;
  • utiliser une version de Python plus adaptée ;
  • corriger une contrainte trop stricte.

Éviter de contourner aveuglément avec :

--no-deps

Cette option installe le paquet sans ses dépendances et peut créer un environnement incohérent.

56. Failed building wheel

Diagnostic :

python -m pip install -vvv package

Tester l’existence d’un binaire :

python -m pip install --only-binary=:all: package

Si aucune distribution n’est trouvée, il n’existe probablement pas de wheel compatible.

Choix possibles :

  • installer les outils de compilation ;
  • utiliser une autre version de Python ;
  • utiliser une autre architecture ;
  • attendre une wheel ;
  • utiliser une version antérieure du paquet ;
  • utiliser Conda pour les bibliothèques scientifiques complexes ;
  • construire une wheel sur une machine de build.

57. externally-managed-environment

Sur certaines distributions Linux :

error: externally-managed-environment

Cela signifie que le Python système est administré par le gestionnaire de paquets de la distribution.

Bonne solution :

python3 -m venv .venv
source .venv/bin/activate
python -m pip install package

Pour une application CLI :

pipx install outil

ou :

uv tool install outil

Éviter :

sudo pip install ...

et éviter autant que possible :

--break-system-packages

car cette option autorise précisément la modification risquée de l’environnement système.

58. Le paquet est installé mais VS Code ne le voit pas

VS Code utilise peut-être un autre interpréteur.

Dans VS Code :

Ctrl + Shift + P
Python: Select Interpreter

Choisir :

...\projet\.venv\Scripts\python.exe

ou :

.../projet/.venv/bin/python

Vérification dans le terminal intégré :

python -c "import sys; print(sys.executable)"

Le terminal, le débogueur, les notebooks Jupyter et l’analyseur de code peuvent chacun utiliser un interpréteur différent.

Partie 15 — Jupyter et environnements

59. Le piège des kernels

Un notebook peut afficher un environnement différent du terminal.

Dans une cellule :

import sys
print(sys.executable)

Pour enregistrer l’environnement comme kernel :

python -m pip install ipykernel
python -m ipykernel install --user --name mon-projet --display-name "Python - mon-projet"

Dans Jupyter, sélectionner ensuite :

Python - mon-projet

Pour installer depuis le notebook, préférer :

import sys
!{sys.executable} -m pip install requests

ou, dans les environnements Jupyter compatibles :

%pip install requests

plutôt que :

!pip install requests

qui peut appeler un autre pip.

Partie 16 — Paquets, modules, distributions : trois notions différentes

60. Distribution PyPI versus module importé

Le nom installé et le nom importé peuvent différer.

Exemples :

python -m pip install pillow

Puis :

from PIL import Image

Autre exemple :

python -m pip install beautifulsoup4

Puis :

from bs4 import BeautifulSoup

Autre exemple :

python -m pip install scikit-learn

Puis :

import sklearn

Il faut distinguer :

  • le nom de distribution ;
  • le nom du module ;
  • le nom du paquet Python ;
  • le nom de la commande CLI.

61. Vérifier à quelle distribution appartient un module

python -m pip show pillow

En Python :

import PIL
print(PIL.__file__)

Pour lire les métadonnées :

from importlib.metadata import version

print(version("pillow"))

Partie 17 — Architecture et système d’exploitation

62. Vérifier l’architecture

python -c "import platform; print(platform.machine())"

Sous Windows :

python -c "import struct; print(struct.calcsize('P') * 8)"

Résultat :

64

indique un Python 64 bits.

Il est possible d’avoir :

  • Windows 64 bits ;
  • mais Python 32 bits.

Dans ce cas, seules les wheels win32 seront acceptées, pas les wheels win_amd64.

63. ARM contre x86-64

Architectures courantes :

x86_64
AMD64
aarch64
arm64

Exemples :

  • PC Intel/AMD Windows : généralement AMD64 ;
  • serveur DigitalOcean classique : souvent x86_64 ;
  • Mac Apple Silicon : arm64 ;
  • Raspberry Pi 64 bits : aarch64.

Une wheel x86-64 ne s’installe pas nativement sur ARM64.

64. Linux glibc contre musl

De nombreuses distributions Linux utilisent glibc.

Alpine Linux utilise généralement musl.

Une wheel :

manylinux_2_17_x86_64

vise un environnement Linux compatible glibc.

Une wheel :

musllinux_1_2_x86_64

vise musl.

C’est pourquoi les images Docker Alpine provoquent parfois davantage de compilations natives que les images Debian slim.

Pour des projets Python contenant beaucoup de dépendances natives, cette image est souvent plus simple :

FROM python:3.12-slim

qu’une image :

FROM python:3.12-alpine

Partie 18 — Conda et bibliothèques scientifiques

65. Quand utiliser Conda ?

pip installe principalement des distributions Python.

Conda peut gérer :

  • Python ;
  • les paquets Python ;
  • les bibliothèques système ;
  • des outils natifs ;
  • CUDA ;
  • BLAS ;
  • certains compilateurs.

Conda peut être pertinent pour :

  • data science ;
  • calcul scientifique ;
  • géospatial ;
  • bio-informatique ;
  • environnements CUDA complexes ;
  • dépendances natives difficiles.

Exemple :

conda create -n projet python=3.12
conda activate projet
conda install numpy pandas scipy

Éviter de mélanger sans stratégie :

conda install
pip install

Approche prudente :

  1. installer d’abord les dépendances Conda ;
  2. installer ensuite avec pip uniquement les paquets absents de Conda ;
  3. éviter de réinstaller via pip une bibliothèque native déjà gérée par Conda.

Gestion des paquets en Python 2/4

Partie 6 — Comprendre les wheels

20. Qu’est-ce qu’une wheel ?

Une wheel est un format de distribution construit, portant l’extension :

.whl

Elle peut contenir :

  • du code Python ;
  • des métadonnées ;
  • des ressources ;
  • des bibliothèques compilées ;
  • des extensions natives.

L’intérêt principal est d’éviter de compiler le logiciel pendant l’installation.

On peut comparer :

sdist  = code source à construire
wheel  = paquet déjà construit

21. Anatomie d’un nom de wheel

Exemple :

numpy-2.1.0-cp312-cp312-win_amd64.whl

Décomposition :

numpy       nom du paquet
2.1.0       version
cp312       interpréteur Python
cp312       ABI
win_amd64   plateforme

Autre exemple :

requests-2.32.3-py3-none-any.whl

Décomposition :

py3    compatible avec Python 3
none   aucune ABI native spécifique
any    n’importe quelle plateforme

Cette seconde wheel est dite « pure Python ».

22. Les trois tags d’une wheel

Une wheel utilise généralement cette structure :

{python tag}-{ABI tag}-{platform tag}

Python tag

Exemples :

py3
cp311
cp312
cp313

cp312 signifie CPython 3.12.

ABI tag

L’ABI est l’interface binaire entre le code compilé et Python.

Exemples :

none
cp312
abi3

Une extension utilisant l’ABI stable peut porter :

abi3

Elle peut alors fonctionner sur plusieurs versions de CPython.

Platform tag

Exemples :

win_amd64
win32
macosx_11_0_arm64
manylinux2014_x86_64
manylinux_2_17_aarch64
musllinux_1_2_x86_64
any

23. Pourquoi une wheel est refusée

Exemple :

ERROR: package.whl is not a supported wheel on this platform

Causes possibles :

  • wheel Python 3.11 utilisée avec Python 3.12 ;
  • wheel Windows utilisée sous Linux ;
  • wheel x86-64 utilisée sur ARM ;
  • wheel ARM utilisée sur x86-64 ;
  • wheel CPython utilisée avec PyPy ;
  • ABI incompatible ;
  • version de pip trop ancienne pour reconnaître les tags ;
  • système Linux incompatible avec le niveau manylinux.

Exemple incompatible :

torch-2.4.0-cp312-cp312-manylinux2014_aarch64.whl

Cette wheel cible CPython 3.12, Linux et l’architecture ARM64. Elle ne conviendra pas à un Windows x86-64. Les tags de wheels peuvent encoder la version Python, l’implémentation, le système d’exploitation et l’architecture.

24. Voir les tags acceptés par son environnement

python -m pip debug --verbose

La sortie contient une liste de tags compatibles, par exemple :

cp312-cp312-win_amd64
cp312-abi3-win_amd64
cp311-abi3-win_amd64
py3-none-any

Cela permet de comparer directement l’environnement avec le nom de la wheel.

Partie 7 — Archives source et compilation

25. Qu’est-ce qu’une sdist ?

Une source distribution utilise souvent :

.tar.gz

Exemple :

package-1.2.0.tar.gz

Elle contient le code source et les informations permettant de construire le paquet.

Lorsque aucune wheel compatible n’est disponible, pip peut télécharger la sdist puis tenter de construire une wheel localement.

On voit alors des messages comme :

Building wheel for package...

ou :

Failed building wheel for package

26. Pourquoi une compilation échoue

Un paquet peut contenir du :

  • C ;
  • C++ ;
  • Rust ;
  • Fortran ;
  • Cython.

Il peut donc nécessiter :

  • un compilateur ;
  • un linker ;
  • des fichiers d’en-tête ;
  • le SDK du système ;
  • des bibliothèques natives ;
  • Rust et Cargo ;
  • CMake ;
  • Ninja ;
  • pkg-config.

Sous Windows

Selon le paquet :

  • Microsoft C++ Build Tools ;
  • Windows SDK ;
  • CMake ;
  • Rust.

Sous Debian ou Ubuntu

Exemple générique :

sudo apt update
sudo apt install build-essential python3-dev

Selon le paquet :

sudo apt install libpq-dev
sudo apt install libssl-dev
sudo apt install libffi-dev
sudo apt install libxml2-dev libxslt1-dev

Exemple PostgreSQL

Le paquet psycopg ou certaines variantes de psycopg2 peuvent nécessiter les bibliothèques PostgreSQL de développement.

Erreur typique :

pg_config executable not found

Solution Debian/Ubuntu :

sudo apt install libpq-dev

27. Forcer l’utilisation de wheels

Pour refuser toute compilation :

python -m pip install --only-binary=:all: package

Exemple :

python -m pip install --only-binary=:all: numpy

Si aucune wheel compatible n’existe, l’installation échoue immédiatement.

C’est utile pour savoir si le problème vient de la compilation.

Préférer les wheels mais autoriser les sources :

python -m pip install --prefer-binary package

Refuser les wheels :

python -m pip install --no-binary=:all: package

Cette dernière commande force une compilation depuis les sources.

28. Construire ses propres wheels

python -m pip wheel -r requirements.txt -w wheels/

Puis installation locale :

python -m pip install --no-index --find-links=wheels -r requirements.txt

Cas d’usage :

  • déploiement hors ligne ;
  • CI/CD ;
  • serveurs sans compilateur ;
  • accélération des déploiements ;
  • contrôle des artefacts installés.

Partie 8 — Résolution des dépendances

29. Dépendances directes et transitives

Supposons :

mon-projet
└── fastapi
    ├── starlette
    ├── pydantic
    └── typing-extensions

fastapi est une dépendance directe.

starlette, pydantic et typing-extensions sont des dépendances transitives.

Lorsqu’on demande :

python -m pip install fastapi

pip doit chercher un ensemble de versions compatibles pour toutes ces bibliothèques.

Le processus qui détermine les versions à installer est appelé résolution des dépendances.

30. Exemple de conflit

Un projet demande :

package-a dépend de common-lib >=2,<3
package-b dépend de common-lib >=3,<4

Aucune version de common-lib ne peut satisfaire les deux contraintes.

pip peut afficher :

ResolutionImpossible

Ce message ne signifie pas nécessairement que pip est cassé.

Il signifie souvent que les contraintes sont mathématiquement incompatibles.

Les versions récentes du résolveur sont volontairement plus strictes face aux dépendances contradictoires.

31. Syntaxe des contraintes de versions

requests==2.32.3
requests>=2.30
requests>=2.30,<3
requests~=2.32.0
requests!=2.32.1

Version exacte

requests==2.32.3

Version minimale

requests>=2.30

Intervalle

requests>=2.30,<3

Version compatible

requests~=2.32.0

Cela signifie approximativement :

>=2.32.0,<2.33.0

Alors que :

requests~=2.32

signifie approximativement :

>=2.32,<3.0

32. Version de Python dans pyproject.toml

[project]
requires-python = ">=3.11,<3.13"

Cela indique que le projet accepte :

Python 3.11
Python 3.12

mais pas :

Python 3.10
Python 3.13

Ce champ est essentiel pour empêcher l’installation du projet avec une version incompatible.

33. Environment markers

Une dépendance peut être conditionnelle :

dependencies = [
    "importlib-metadata; python_version < '3.10'",
    "colorama; sys_platform == 'win32'",
]

Ainsi :

  • colorama sera installé sous Windows ;
  • importlib-metadata ne sera installé que pour certaines versions de Python.

Les résolveurs prennent en compte :

  • python_version ;
  • python_full_version ;
  • sys_platform ;
  • platform_machine ;
  • platform_python_implementation.

Partie 9 — requirements.txt, contraintes et verrouillage

34. Le fichier requirements.txt

Exemple simple :

fastapi
uvicorn
sqlalchemy
psycopg

Installation :

python -m pip install -r requirements.txt

Mais ce fichier ne garantit pas nécessairement une reproduction exacte.

Aujourd’hui, il peut installer :

fastapi 0.x
pydantic 2.x

Dans six mois, il peut choisir d’autres versions.

35. Versions figées

fastapi==0.116.1
uvicorn==0.35.0
sqlalchemy==2.0.41

Cela améliore la reproductibilité.

Pour exporter l’environnement actuel :

python -m pip freeze > requirements.txt

Mais pip freeze exporte généralement tout ce qui est installé, y compris les dépendances transitives.

Exemple :

fastapi==...
pydantic==...
pydantic-core==...
starlette==...
typing-extensions==...

Ce n’est pas toujours un bon fichier à maintenir manuellement.

36. Différence entre dépendances directes et environnement figé

Il est utile de distinguer :

Dépendances voulues

fastapi
sqlalchemy
psycopg

Résultat exact de la résolution

fastapi==...
starlette==...
pydantic==...
sqlalchemy==...
psycopg==...
typing-extensions==...

Le premier fichier décrit l’intention.

Le second décrit l’environnement résolu.

Les outils modernes comme uv, Poetry ou PDM maintiennent cette distinction avec :

  • pyproject.toml ;
  • un fichier de verrouillage.

37. Fichier de contraintes

Exemple constraints.txt :

urllib3<2.3
pydantic==2.11.7

Installation :

python -m pip install -r requirements.txt -c constraints.txt

Une contrainte ne demande pas nécessairement l’installation du paquet.

Elle limite la version si ce paquet doit être installé.

Gestion des paquets en Python 1/4

Gestion des versions et des paquets en Python

1. Le modèle mental indispensable

Les difficultés viennent souvent du fait que l’on confond plusieurs éléments indépendants :

  1. la version de Python ;
  2. l’interpréteur Python réellement exécuté ;
  3. l’environnement Python actif ;
  4. l’outil d’installation des paquets ;
  5. les versions des bibliothèques ;
  6. les formats de distribution comme les wheels ;
  7. le système d’exploitation et l’architecture du processeur.

Lorsqu’on exécute :

pip install pandas

la vraie question n’est pas seulement :

Quelle version de pandas sera installée ?

Il faut aussi demander :

Quel pip est exécuté, à quel interpréteur Python appartient-il, dans quel environnement installe-t-il, et existe-t-il un fichier binaire compatible avec mon système ?

Partie 1 — Une installation Python n’est pas un simple exécutable

2. Qu’est-ce qu’une version de Python ?

Une installation Python contient notamment :

  • un interpréteur ;
  • la bibliothèque standard ;
  • des fichiers de configuration ;
  • un répertoire destiné aux paquets externes ;
  • éventuellement pip ;
  • parfois des bibliothèques natives ;
  • des en-têtes C pour compiler des extensions.

L’exécutable peut porter différents noms :

python
python3
python3.11
python3.12
python.exe

Sous Windows, on peut également disposer du lanceur Python :

py

Le lanceur py est particulièrement utile lorsque plusieurs versions sont installées.

Exemples :

py --list
py -3.11 --version
py -3.12 --version
py -3.11 script.py

Une version Python est constituée de l’interpréteur, de la bibliothèque standard et de ses fichiers associés.

3. Version majeure, mineure et corrective

Dans :

Python 3.12.7

on distingue :

3      version majeure
12     version mineure
7      version corrective

Les incompatibilités de paquets concernent principalement la version mineure.

Par exemple :

Python 3.10
Python 3.11
Python 3.12
Python 3.13

ne doivent pas être considérés comme quatre mises à jour parfaitement interchangeables.

Une bibliothèque peut déclarer :

Requires-Python >=3.10,<3.13

Elle acceptera Python 3.10, 3.11 et 3.12, mais refusera Python 3.13.

À l’inverse, les versions correctives sont normalement compatibles entre elles :

3.12.1
3.12.7
3.12.10

Un environnement virtuel créé avec Python 3.12.1 reste généralement utilisable avec les correctifs de la branche 3.12, même s’il est souvent préférable de le recréer après un changement important de l’installation système.

4. CPython n’est pas l’unique implémentation de Python

L’implémentation utilisée dans l’immense majorité des projets est CPython.

Il existe cependant d’autres implémentations :

  • PyPy ;
  • GraalPy ;
  • MicroPython ;
  • Jython ;
  • IronPython.

Lorsqu’une wheel contient le tag :

cp312

cela signifie généralement :

CPython 3.12

Une wheel cp312 ne vise pas nécessairement PyPy 3.12.

Vérification :

python -c "import platform; print(platform.python_implementation())"

Résultat typique :

CPython

Partie 2 — Pourquoi plusieurs commandes Python coexistent

5. Le problème du PATH

Lorsque l’on tape :

python

le système cherche un exécutable dans les répertoires inscrits dans la variable PATH.

Sous Windows :

where python
where pip

Sous Linux ou macOS :

which python
which python3
which pip
type -a python
type -a pip

Il est fréquent d’obtenir plusieurs installations :

C:\Users\yvon\AppData\Local\Programs\Python\Python311\python.exe
C:\Users\yvon\AppData\Local\Programs\Python\Python312\python.exe
C:\Windows\py.exe

Le premier chemin trouvé dans PATH est généralement utilisé.

Ainsi, il est possible que :

python

désigne Python 3.12, alors que :

pip

appartienne à Python 3.11.

C’est l’une des causes les plus fréquentes de ce problème :

pip install requests

réussit, mais :

import requests

échoue avec :

ModuleNotFoundError: No module named 'requests'

6. La règle d’or : utiliser python -m pip

Au lieu de :

pip install requests

utiliser :

python -m pip install requests

Ou, sous Windows :

py -3.12 -m pip install requests

Cette syntaxe signifie :

Exécute le module pip avec cet interpréteur Python précis.

Elle garantit que le pip utilisé appartient au Python indiqué.

Commandes de diagnostic :

python --version
python -c "import sys; print(sys.executable)"
python -m pip --version

Exemple :

Python 3.12.7
C:\Python312\python.exe
pip 25.x from C:\Python312\Lib\site-packages\pip

Les trois chemins doivent être cohérents.

7. Diagnostic complet de l’interpréteur actif

Cette commande est particulièrement utile :

python -c "import sys, platform; print('Executable:', sys.executable); print('Version:', sys.version); print('Prefix:', sys.prefix); print('Base prefix:', sys.base_prefix); print('Architecture:', platform.machine())"

Informations importantes :

sys.executable

Chemin de l’interpréteur exécuté :

import sys
print(sys.executable)

sys.version

Version détaillée :

print(sys.version)

sys.prefix

Racine de l’environnement actif.

sys.base_prefix

Racine de l’installation Python d’origine.

Dans un environnement virtuel :

sys.prefix != sys.base_prefix

Test :

python -c "import sys; print(sys.prefix != sys.base_prefix)"

Résultat :

True

signifie généralement qu’un environnement virtuel est actif.

Partie 3 — Installation globale, utilisateur et virtuelle

8. Les trois emplacements principaux

Un paquet peut être installé :

  1. globalement ;
  2. dans le répertoire utilisateur ;
  3. dans un environnement virtuel.

9. Installation globale

Exemple :

python -m pip install requests

sans environnement virtuel.

Le paquet est installé dans le site-packages associé à l’interpréteur global.

Sous Windows :

C:\Python312\Lib\site-packages

Sous Linux :

/usr/local/lib/python3.12/site-packages

ou :

/usr/lib/python3/dist-packages

Pourquoi l’installation globale est problématique

Deux projets peuvent demander des versions différentes :

Projet A : Django 4.2
Projet B : Django 5.2

Un environnement global ne peut généralement contenir qu’une version de Django pour un même interpréteur.

Installer Django 5.2 pour le projet B peut donc casser le projet A.

Autre problème : sur Linux, le Python global peut être utilisé par le système d’exploitation.

Modifier ses paquets peut casser :

  • un gestionnaire de paquets ;
  • des scripts d’administration ;
  • des outils système ;
  • des composants installés par la distribution.

Les distributions modernes peuvent déclarer l’environnement Python système comme « externally managed », afin d’empêcher pip de le modifier directement.

10. Installation utilisateur

Commande :

python -m pip install --user requests

Le paquet est installé dans un répertoire appartenant à l’utilisateur.

Exemple Windows :

C:\Users\yvon\AppData\Roaming\Python\Python312\site-packages

Exemple Linux :

~/.local/lib/python3.12/site-packages

Cette méthode évite les droits administrateur, mais elle n’isole pas les projets.

Tous les projets utilisant ce Python peuvent voir ces paquets.

Elle est donc utile dans certains cas ponctuels, mais elle ne remplace pas un environnement virtuel.

11. Installation dans un environnement virtuel

Un environnement virtuel possède notamment :

  • son propre exécutable Python ou un lien vers celui-ci ;
  • son propre site-packages ;
  • son propre pip ;
  • un fichier pyvenv.cfg.

Dans sa forme minimale, un environnement virtuel contient un exécutable ou lien vers Python, un répertoire site-packages et un fichier pyvenv.cfg.

Création :

python -m venv .venv

Le module venv crée l’environnement avec la version de Python utilisée pour lancer la commande.

Ainsi :

python3.11 -m venv .venv

crée un environnement en Python 3.11.

Alors que :

python3.12 -m venv .venv

crée un environnement en Python 3.12.

Un environnement virtuel ne choisit pas lui-même une version de Python.

Il hérite de l’interpréteur qui l’a créé.

Partie 4 — Utilisation correcte de venv

12. Création sous Windows

Avec le lanceur Python :

py -3.12 -m venv .venv

Activation dans PowerShell :

.venv\Scripts\Activate.ps1

Activation dans cmd.exe :

.venv\Scripts\activate.bat

Désactivation :

deactivate

13. Création sous Linux ou macOS

python3.12 -m venv .venv

Activation :

source .venv/bin/activate

Désactivation :

deactivate

14. Ce que fait réellement l’activation

L’activation ne démarre pas une machine virtuelle.

Elle modifie principalement la variable PATH afin que :

python
pip

désignent en priorité les exécutables situés dans .venv.

Sous Windows :

.venv\Scripts\python.exe
.venv\Scripts\pip.exe

Sous Linux :

.venv/bin/python
.venv/bin/pip

Il est possible d’utiliser un environnement virtuel sans l’activer :

Sous Windows :

.venv\Scripts\python.exe script.py
.venv\Scripts\python.exe -m pip install requests

Sous Linux :

.venv/bin/python script.py
.venv/bin/python -m pip install requests

L’activation est donc un confort, pas une obligation technique.

15. Vérifier l’environnement actif

Sous Windows :

where python
python -c "import sys; print(sys.executable)"
python -m pip --version

Sous Linux :

which python
python -c "import sys; print(sys.executable)"
python -m pip --version

Résultat attendu :

...\mon-projet\.venv\Scripts\python.exe

ou :

.../mon-projet/.venv/bin/python

16. Ne jamais versionner .venv

Dans .gitignore :

.venv/
venv/
env/
__pycache__/
*.pyc

Un environnement virtuel :

  • contient des chemins absolus ;
  • dépend du système d’exploitation ;
  • dépend de l’architecture ;
  • peut contenir des binaires natifs ;
  • peut devenir très volumineux.

Il ne doit donc pas être copié entre machines ni ajouté à Git.

On versionne les fichiers décrivant les dépendances, pas l’environnement lui-même.

17. Recréer un environnement propre

Lorsqu’un environnement devient incohérent, le réparer paquet par paquet est souvent une mauvaise stratégie.

Approche propre :

Sous PowerShell :

deactivate
Remove-Item -Recurse -Force .venv
py -3.12 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

Sous Linux :

deactivate
rm -rf .venv
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

Partie 5 — Que fait réellement pip install ?

18. Les grandes étapes

Lorsqu’on lance :

python -m pip install pandas

pip doit notamment :

  1. identifier la demande ;
  2. lire les métadonnées du projet ;
  3. vérifier la version de Python ;
  4. résoudre les dépendances ;
  5. choisir les versions compatibles ;
  6. sélectionner un fichier de distribution ;
  7. télécharger une wheel ou une archive source ;
  8. éventuellement compiler le paquet ;
  9. installer les fichiers ;
  10. créer les scripts exécutables ;
  11. enregistrer les métadonnées d’installation.

La documentation de pip install décrit notamment les étapes d’identification des exigences, de résolution des dépendances, de construction des wheels puis d’installation.

19. PyPI

PyPI, Python Package Index, est le dépôt public principal de paquets Python. Les auteurs y publient leurs distributions et pip peut y rechercher les fichiers adaptés.

Une page PyPI peut proposer plusieurs fichiers pour une seule version :

package-1.0.0.tar.gz
package-1.0.0-py3-none-any.whl
package-1.0.0-cp312-cp312-win_amd64.whl
package-1.0.0-cp312-cp312-manylinux_2_17_x86_64.whl

Ces fichiers ne sont pas interchangeables.

La raison est que le même paquet doit pouvoir fonctionner dans des contextes très différents. Chaque fichier correspond à un cas précis :

  • le fichier .tar.gz est le code source, utilisé quand aucune version précompilée n’est disponible ;
  • le fichier py3-none-any.whl est une version pure Python, compatible avec tous les systèmes ;
  • les fichiers cp312-... sont des versions compilées pour une version précise de Python (ici CPython 3.12) ;
  • les suffixes win_amd64 ou manylinux_2_17_x86_64 indiquent le système d’exploitation et l’architecture.

En résumé, plusieurs versions existent pour garantir que pip puisse installer le bon fichier selon :

  • ta version de Python,
  • ton système d’exploitation,
  • ton architecture matérielle,
  • et la présence ou non de code compilé.

C’est ce mécanisme qui permet à un même paquet d’être utilisable sur presque toutes les machines, sans modification manuelle.

Reconstruire le venv de python pour corriger les problème de dépendances

Récemment j’ai rouvert mon Pycharm après quasiment 2 ans d’inactivité, et en plus j’ai mis à jour la version de Pycharm de la version 2020 à 2024. Toutes les dépendances ont sauté, impossible d’exécuter les programme, il y avait aussi un problème de chemin d’interpréteur auquel je ne faisait pas attention. Désinstaller et réinstaller les module ne résouds pas le problème, il faut détruire et reconstruire le venv, qui est essentiellement un environnement de gestion des dépendances.

Qu’est ce qu’un environnement virtuel?

C’est un répertoire qui contient les librairies que votre projet va utiliser. Cet environnement est spécifique à votre projet. Il contient des exécutables pour les opérations de maintenance et utilitaires. Si vous deviez reproduire le projet sur un autre ordinateur, il faut faire un fichier requirements.txt qui contiendra les références à toutes les librairie installées, en NodeJS on a package.json, en PHP on a composer.json.

Détruire l’environnement virtuel

Il suffit simplement d’effacer le répertoire venv.

Reconstruire l’environnement venv

Mettez vous dans le répertoire projet

# windows
python -m venv venv

Linux/MacOs
python3 -m venv venv

Opération sur les package et l’environnement virtuel

Cela peut être un peu compliqué à gérer surtout quand vous avez des problème de charge de paquets (ou dépendances). Il faut savoir qu’il y a plusieurs installations de Python, dans le venv, dans un coin de votre disque dur, etc.

Localisation des paquets globaux

pip list -v  # pour lister 

Activation de l’environnement virtuel

Il faut exécuter un fichier activate.bat (windows) qui se trouve dans le répertoire venv/scripts

tapez activate.bat ou activate  sous windows

dans Linux
source venv/bin/activate

une fois activé, le prompt est visible
(venv) $

Si l’environnement n’est pas activé, vous ne pourrez pas accéder à pip indirectement, il faudra indiquer le chemin jusqu’à lui.

Lister les package dans le fichier requirements

pip freeze > requirements

Installer ou mettre à jour les paquets depuis le fichier requirements.txt

pip install -r requirements

Scraper le web avec Selenium en Python

Contexte : je suis sous MacOS en architecture Intel 64bit (processeur Core i5)

Selenium vous permet programmatiquement d’émuler un navigateur (ici ce sera Chrome), et de télécharge une page et de l’analyser. Pourquoi est ce que c’estintéressant? Pour les test d’interface d’une part, puis d’autre part si vous voulez scraper une page très javascript, où les éléments sont rendus en JS donc non récupérables par un scrap avec BeautifulSoup, Selenium le peut.

Il est juste plus lent que BeautifulSoup, en effet il se repose sur le navigateur Chrome (notre cas ici mais ça peut être n’importe quel navigateur) qui fait le rendu, alors que BeautifulSoup ne fait pas le rendu.

Pré-requis :

  • connaissez votre version de navigateur Chrome, en allant dans les 3 petits point puis Aide puis A Propos de Google Chrome
  • Déterminez l’architecture de votre plateforme
  • Ensuite allez sur le site de Chromium pour télécharger le chromedriver à mettre dans le même répertoire que votre script, le driver permet à Selenium de contrôler Chrome, c’est dépendant de l’OS et du microprocesseur. Il faut suivre le lien pour les dernières versions de Chrome Vous trouverez aussi les zip de Chrome for testing, que Selenium va lancer. chromedriver et Chrome for Testing doivent être téléchargés ensemble.
  • Installer la librairie Selenium pip install selenium

Script minimal de Selenium pour scraper une page

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

browser = webdriver.Chrome()   #mac https://stackoverflow.com/questions/76928765/attributeerror-str-object-has-no-attribute-capabilities-in-selenium
#driver = webdriver.Chrome('./chromedriver'). #ne marche pas sur Mac

browser.get("https://www.python.org")

print(browser.title)

Mettre en place un environnement Python dans VSCode

Installer l’extension Python Environments

Cette extension est nécesssaire pour pouvoir avoir accès à la barre de commande

CTRL + SHIFT + P puis Python: Create Environment et sélectionnez la commande.

Choisissez l’interpréteur

Ensuite ça prend un peu de temps pour créer l’espace virtuel, isolé de développement Python.

Pour s’assurer de la version de l’interpréteur Python, dans la palette de commande (CTRL + SHIFT + P) puis Python: Select Interpreter

Maintenant vous êtes prêt pour commencer à coder en Python dans VSCode.

Créez un fichier hello.py,

print("hello")

Pour exécuter, appuyez sur le triangle en haut à droite de VSCode, liste déroulante choisir Debug Python File. Notez le point rouge qui est le point d’arrêt.

Debugger python sous VScode

Il suffit de mettre un point d’arrêt et de cliquer sur le symbole debug dans la barre latérale. Une autre méthode est d’utiliser le menu contextuel et démarrer le script.

Pour plus d’information ce lien.

Comment télécharger une vidéo Wistia avec le langage Python

La plateforme Wistia est un hébergeur de vidéo, souvent utilisé dans les sites en accès restreint (pour la formation par exemple). Parfois on a envie de télécharger les vidéo pour regarder plus tranquillement quan on est en vacances dans un endroit où l’internet n’est pas très développé. Il vous faut télécharger les vidéos.

Nous allons voir comment on peut le faire ne Python. Pour ce faire vous devez avoir installé un environnement python et avoir déjà manipulé ce langage.

Le dépôt du projet Wisty : https://pypi.org/project/wisty/

Télécharger la librairie Wisty

pip install wisty

Extraire l’identifiant de la vidéo

sur la vidéo, faites un clic droit, ou deux clic droit pour faire apparaitre la popup qui vous permet de copier le code HTML d’intégration de la vidéo. Collez ce code dans un éditeur de texte, copiez la portion qui est juste après l’url de la page

<a href="https://www.lesite.com?wvideo=nts3yvnjlb">

Ici c’est nts3yvnjlb, nous allons avoir beosin de cet identifiant dans la commande en ligne

Ecrire la ligne de commande qui permet de télécharger la vidéo

Si vous utilisez le terminal de Pycharm, dans le projet ou le venv dans lequel vous avez installé Wisty, vous pouvez directement taper cette commande:

wisty -i nts3yvnjlb-r 720p -n nom-fichier-en-local

Si vous voulez faire la même chose depuis un prompt DOS :

C:\Users\admin\PycharmProjects\pythonProject\venv\Scripts\wisty -i nts3yvnjlb-r 720p -n nom-fichier-en-local

Assurez vous que le nom du projet (pythonProject) est bien celui où vous avez installé la librairie.

Comment économiser de l’espace mémoire

Le paramère 720p concerne la résolution de la vidéo que vous téléchargez, la définition 1080p est vraiment dix fois plus lourde que la 720p, qui reste pour certains types de vidéo de très bonne qualité.

Optimiser vos téléchargement avec un script batch sous Windows

Par exemple sur Windows vous pouvez créer un fichier .bat et y mettre les multiple commandes de téléchargements de vidéo. Mettez de préférence les commandes avec le chemin absolu, une comande par ligne, ensuite il ne vous reste plus qu’à lancer le fichier .bat pour télécharger tranquillement vos vidéos.

Utiliser un socket en Python pour lire les cours sur Binance

A quoi servent les sockets?

Les sockets vous permettent d’ouvrir une liaison continue entre le serveur et le client. A la différence d’une requête AJAX, qui est l’équivalent d’un chargement de page HTTP, qui s’arrête une fois les informations téléchargées, le socket ouvre un stream continu. Cela évite les limitations des requêtes AJAX, comme par exemple la limitation du nombre de requêtes par minutes par exemple.

Si vous voulez connaitre un cours de bourse en temps réel, hors de question d’utiliser des requêtes AJAX, car vous allez vous faire blacklister.

Installer la librairie websocket-client

Il vous faut installer la librairie websocket-client (attention il y a une autre librairie qui s’appelle websocket, ce n’est pas celle là)

pip install websocket-client

Le script:

import websocket,json,os
def on_message(ws, message):
    """ convert string to dictionary"""
    message = json.loads(message)
    clear = lambda: os.system('cls')
    clear()
    print(message)
    print(" ",message['data'][0]['p'],flush=True,end="\r")


def on_error(ws, error):
    print(error)

def on_close(ws, close_status_code, close_msg):
    print("### closed ###")

def on_open(ws):
    ws.send('{"method":"SUBSCRIBE","params":["btcusdt@aggTrade","btcusdt@depth"],"id":1}')

if __name__ == "__main__":
    websocket.enableTrace(False)
    ws = websocket.WebSocketApp(f"wss://stream.binance.com:9443/ws/bnbbtcopen@depth",
                              on_open=on_open,
                              on_message=on_message,
                              on_error=on_error,
                              on_close=on_close)
    ws.on_open = on_open
    ws.run_forever()

Lien vers la documentation du service websocket de Binance:

Python Module not found en exécution en ligne de commande

Alors que ça marchait très bien sous PyCharm…

Imaginons que vous ayez un programme utilisant une librairie externe, par exemple Beautifulsoup, ce dernier étant une librairie externe, donc il faut l’importer avec la commande :

pip install beautifulsoup4

Sous PyCharm, la librairie sera installée dans la section External Librairies, dans la barre latérale Project. Mais le véritable répertoire (sous Windows) sera :

C:\Users\mon_user\PycharmProjects\nom_projet\venv\Lib\site-packages

Remplacez mon_user et nom_projet par vos données propres.

Ainsi lorsque vous exécutez un programme en ligne de commande, et qu’il vous dit « Module Not Found …bs4 » c’est que l’exécutable Python n’arrive pas à le trouver. Il faut créer la variable PYTHONPATH.

Pour ce faire (Windows) dans la boite de recherche de Windows (Cortana) tapez PATH, vous accédez aux variables d’environnement. Ajoutez la nouvelle variable PYTHONPATH puis ajoutez le chemin cité plus haut. MAintenant vous pouvez lancer votre programme en ligne de commande.

Lister les valeurs des chemins d’inclusion

import sys
print(sys.path)

Ce programme va vous afficher tous les chemins qui sont tenus compte pour la recherche de module. En fonction de votre environnement virtuel, les résultats peuvent différer, et vous saurez pourquoi vous avez un Module Not Found.

Python : nombre d’argument arbitraire avec **kwargs et *args

En python il est possible de construire des fonctions qui acceptent un nombre indéfini de paramètres, aussi bien en positionnel qu’en nommé.

*args

def mafonction(*args):
   for a in args:
        print(a)

mafonction(1)
# 1
mafonction(1,2,3)
# 1
# 2
# 3

le *args est interprété comme un tuple

**kwargs

def mafonction(param,*args,**kwargs):
    for a in kwargs:
        print(a,kwargs[a])

mafonction(nom="Dupont",age=27)
#nom Dupont
#age 27

Notez qu’il n’est pas nécessaire de les appeler *args et **kwargs, on peut utiliser d’autres noms.

On peut mixer ces deux types de paramètre avec des paramètres classiques

def mafonction(param1,*args,**kwargs):
    

Unpack argument list

Cette technique consiste à préfixer le paramètre d’un astérisque, pas lors de la déclaration de la fonction, mais lors de l’appel de la fonction.

def mafonction(a,b,c):
    print(a,b,c)

obj = {"nom":"Dupont","age":27}
mafonction(100,**obj)
# 100,Dupont 27

def mafonction(a,b):
    print(a,b)

l = [1,2]
mafonction(*l)
# 1 2

Pour les listes et tuple on utilisera *args, pour les dictionnaire on utilisera **

mylist = [1,2,3]
>>> foo(*mylist)
x=1
y=2
z=3

>>> mydict = {'x':1,'y':2,'z':3}
>>> foo(**mydict)
x=1
y=2
z=3

>>> mytuple = (1, 2, 3)
>>> foo(*mytuple)
x=1
y=2
z=3

Extended iterable unpacking

Une façon de faire de l’unpacking puissante

first, *rest = [1,2,3,4]
first,*l,last = [1,2,3,4]

Enfin un esyntaxe propre à Python 3 :

def mafonction(arg1,arg2,*,kwarg1,kwarg2):
    pass

La syntaxe ci-dessus impose que les deux premiers paramètres soient positionnel, et les paramètre après l’astérisque soient nommés.

https://stackoverflow.com/questions/36901/what-does-double-star-asterisk-and-star-asterisk-do-for-parameters

Python : tester si une variable est un nombre avec isdigit ()

Le problème de conversion d’une chaine en nombre entier

Lorsqu’on a une chaine de caractères qu’on cherche à convertir en nombre, il faut se poser la question de si c’est faisable.

Imaginons qu’on ait une variable à tester dont le type est un string :

ns = "125"
n = int(ns)

Mais si on a une chaine de caractère qui ne contient pas purement des chiffres (voire une chaine vide):

nm1 = "12.3"
nm2 = "z123"
nm3 = ""

Tenter de convertir ces chaines en entier déclenchera une erreur de type ValueError.

Tester si une chaine est un nombre entier avec isdigit()

En Python tout est objet, un nombre entier est un objet, un booléen est un objet, un float est un objet,(vous pouvez le voir en utilisant la notation pointée). Ces objets ont des méthodes (c’est le nom en OOP pour une fonction). Et dans le cas des chaines de caractères, uen de ces méthode est isdigit().

n = "123"
print(n.isdigit())  # True 
n = ""
print(n.isdigit())   # False
#on peut encore écrire
print("123".isdigit())  #True

Ainsi avant de convertir en entier, testez la chaine avec isdigit() avant d’utiliser int()

if("123".isdigit()):
    n = int("123")

Connexion à MySQL en Python

Installez au préalable Mysql, le plus rapide (avec zéro configuration ) étant d’installer la solution de développement PHP Laragon si vous êtes sous windows.

Ensuite il faudra redéfinir le mot de passe root, allez dans Laragon click droit sur le bouton Database > Mysql > Change root password.

Connexion à Mysql en Python

from mysql.connector import MySQLConnection, Error

def connect():
    """ Connect to MySQL database """
    conn = None
    try:
        conn = mysql.connector.connect(host='localhost',
                                       database='boutique',
                                       user='root',
                                       password='root')
        if conn.is_connected():
            print('Connected to MySQL database')

    except Error as e:
        print(e)

    finally:
        if conn is not None and conn.is_connected():
            conn.close()

if __name__ == '__main__':
    connect()

Exécuter une requête SQL en Python

from mysql.connector import MySQLConnection, Error

def query_with_fetchone():
    try:

        conn = MySQLConnection(host='localhost',
                                         database='boutique',
                                         user='root',
                                         password='root')
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM staffs")

        row = cursor.fetchone()

        while row is not None:
            print(row)
            row = cursor.fetchone()

    except Error as e:
        print(e)

    finally:
        cursor.close()
        conn.close()


query_with_fetchone()

Connexion à Sqlite en Python

Sqlite est une base de donnée un peu spéciale, puisque la base tient dans un fichier binaire, avec extension .db, c’est donc quelque chose de très simple, et Sqlite est destiné à être utilisé sur des systèmes embarqués.

Donc pas besoin d’installer un programme, c’est la librairie Python qui va s’ocuper de cela. De plus c’est dans la librairie standard, ce qui veut dire que l’on n’ pas besoin de faire un pip install.

La librairie Python sqlite3

Il suffit tout simplement d’importer la librairie sqlite3, de créer une connexion pour créer le fichier binaire de la base de données

import sqlite3
from sqlite3 import Error


def create_connection(db_file):
    """ crée une connexion SQLite si lea DB n'existe pas elle sera créée"""
    conn = None
    try:
        conn = sqlite3.connect(db_file)
        print(sqlite3.version)
    except Error as e:
        print(e)
    finally:
        if conn:
            conn.close()


if __name__ == '__main__':
    create_connection(r"C:\db\pythonsqlite.db")

Sivous exécutez pour la première fois ce script, le fichier pythonslqite.db sera créé, par contre il vous faudra avoir le répertoire déjà créé, sinon vous aurez une erreur. Vous pouvez aussi créer une base de données avec DB Browser, un browser de base sqlite.

Requêter dans Sqlite en Python

Voici la recette pour faire une requête SQL en Python dans Sqlite. On acréé la base de données test.db, et il y a une table items. Vous pouvez importer dans DBBrowser le fichier contenant les données ci-dessous ou exécuter le SQL suivant :

BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "items" (
	"id"	INTEGER,
	"nom"	TEXT,
	"prix"	INTEGER
);
INSERT INTO "items" VALUES (1,'Pomme',5);
INSERT INTO "items" VALUES (1,'Poire',3);
INSERT INTO "items" VALUES (1,'raisin',13);
COMMIT;
import sqlite3
from sqlite3 import Error

def create_connection(db_file):
    conn = None
    try:
        conn = sqlite3.connect(db_file)
    except Error as e:
        print(e)

    return conn

def select_items(conn):
    """
    Query all rows in the tasks table
    :param conn: the Connection object
    :return:
    """
    cur = conn.cursor()
    cur.execute("SELECT * FROM items")

    rows = cur.fetchall()

    for row in rows:
        print(row)

db_file = "c:\\Users\\admin\\Documents\\test.db"
conn = create_connection(db_file)
select_items(conn)

Compréhension de liste en Python

Base de compréhension de liste

list = [i for i in range(5)]
print(list)  # [0,1,2,3,4]
list2 = [ i * i for i in range(10)]
print(list2) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

If dans compréhension de liste

list3 = [ n for n in range(10) if n % 2 == 0]
print(list3) # [0, 2, 4, 6, 8]

If imbriqués
list4 = [ n for n in range(10) if n % 2 == 0 if n >= 5]
print(list4) # [6, 8]

If…Else dans compréhension de liste

list5 = ["Even" if i%2==0 else "Odd" for i in range(10)]
print(list5) #

list6 = [str(i) + ":Pair" if i%2==0 else str(i) + ":Impair" for i in range(10)]
print(list6) 
# ['0:Pair', '1:Impair', '2:Pair', '3:Impair', '4:Pair', '5:Impair', '6:Pair', '7:Impair', '8:Pair', '9:Impair']

Attribut de classe et d’instance en Python

Attribut de classe

class Dog:
    leg_number = 4
    def __init__(self,name):
        self.name = name

chien1 = Dog("Médor")
print(chien1.leg_number)  # 4
print(Dog.leg_number)     # 4

Dans l’exemple ci-dessus leg_number est un attribut de classe. Pyton va chercher d’abord dans les attribut d’instance pour voir si leg_number existe, sinon va aller chercher dans les attributs de classe.

Attribut d’instance

name est un attribut d’instance car rattaché à self.

class Dog:
    leg_number = 4
    def __init__(self,name):
        self.name = name

chien1 = Dog("Médor")
print(chien1.name)  # Médor
print(Dog.name)     # AttributeError: type object 'Dog' has no attribute 'name'

Python ne trouvera pas name en attribut de classe. Par contre on peut surcharger name en attribut de classe.

class Dog:
    leg_number = 4
    name = "toto"
    def __init__(self,name):
        self.name = name

chien1 = Dog('médor')
print(chien1.name)   # médor
print(Dog.name)      # toto

Un attribut de classe est commun à tous les objets qui en dérivent.

class Dog:
    leg_number = 4
    def __init__(self,name):
        self.name = name

chien1 = Dog('médor')
chien2 = Dog('Lassie')
print(chien1.leg_number)
print(chien2.leg_number)
Dog.leg_number = 5
print(chien1.leg_number)
print(chien2.leg_number)

https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide

Héritage de classe dans Python

A première vue, les notions d’orienté objet sont simple en Python par rapport à Java, mais même si l’encapsulation ,n’existe pas en tant que tel (voir le mangling en Python), il y a beaucoup de choses à apprendre en héritage dans Python.

Il y a les fonctions magiques (avec le dunder) pour faire du polymorphisme et l’héritage multiple comme en C++ !

Classe et héritage simple

class Kite:
    def __init__(self,max_altitude):
        self.max_altitude = max_altitude
    def do(self):
        return "I fly"
    def getMaxAltitude(self):
        return self.max_altitude


class BigKite(Kite):
    def __init__(self,max_altitude,weight):
        self.max_altitude = max_altitude
        self.weight = weight
    def getWeight(self):
        return self.weight

Ici BigKite hérite de Kite, et la méthode de constructeur __init__ est surchargée. Si nous voulons utiliser le constructeur de la classe parente :

class Kite:
    def __init__(self,max_altitude):
        self.max_altitude = max_altitude
    def do(self):
        return "I fly"
    def getMaxAltitude(self):
        return self.max_altitude


class BigKite(Kite):
    def __init__(self,max_altitude,weight):
        super().__init__(max_altitude) #appel classe parente
        self.weight = weight
    def getWeight(self):
        return self.weight

On aurait pu écrire aussi :

super(Kite,self).__init__(max_altitude)

Héritage multiple

Ici KiteSurf hérite de la classe Kite et de la classe Surf, qui elles-même héritent de Object, on est ici en présence de diamond problem

class Object:
    def __init__(self):
        pass
    def do(self):
        return "I exist"


class Kite(Object):
    def __init__(self,max_altitude):
        self.max_altitude = max_altitude
    def do(self):
        return "I fly"
    def getMaxAltitude(self):
        return self.max_altitude


class Surf(Object):
    def __init__(self,matter):
        self.matter = matter
    def do(self):
        return "I surf"
    def getMaxAltitude(self):
        return self.max_altitude

class KiteSurf(Surf,Kite):
    def __init__(self):
        pass

ks = KiteSurf()
print(ks.do())

L’affichage de ks.do() méthode existant dans Kite et dans Surf dépend de l’ordre dans lequel est passé en paramètre les classes parentes.

https://he-arc.github.io/livre-python/super/index.html

Encapsulation dans les classes Python

Il n’existe pas de private ni de protected dans Python, la philosophie de Python est que le développeur est un être responsable.

Cependant Python supporte ce que l’on appelle le mangling.

class Dog:
    __init__(self):
        self.name = "Médor"
        self.__tatoo = "X312B13"

chien1 = Dog()
print(chien1.name) # Médor
print(chien1.__tatoo) # AttributeError: 'Dog' object has no attribute '__tatoo'

__tatoo a été substitué par un autre nom en vertu des deux underscores, on ne connait pas le nouveau nom, ce qui reproduit le process d’encapsulation.

Mécanisme de session de Django

Similaire aux sessions en PHP, dans Django il faut importer deux lignes pour pouvoir travailler avec les sessions:

#settings
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions', // le module Session
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'products.apps.ProductsConfig'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware', // le middleware
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Avec Django par défaut les sessions sont sauvées dans la base de données.

Les formulaires dans Django

Les formulaires sont un vaste chapitre comme dans tout framework web quelque soit le langage. Mais Django permet de les manipuler plus facilement que Symfony par exemple.

cleaned_data qu’est ce que c’est?

Il y a deux types de formulaire dans Django, les formulaires classiques qui héritent de forms.Form, et mes formulaires liés à des entités, qui héritent de forms.ModelForm.

Avec les modelForm, inutile de s’inquiéter du cleaned_data.

Dans un form classique on doit utiliser les données validée:
if form.is_valid():
    ex = Example()
    ex.username = form.cleaned_data['username']
    ex.save()

Alors que dans un modelForm, cette étape est automatique.

if form.is_valid():
    form.save()

https://stackoverflow.com/questions/53594745/what-is-the-use-of-cleaned-data-in-django

Upload de fichiers dans Django

Pour uploader un fichier dans un formulaire, il faut modifier le model,

class Product(models.Model):
    name = models.CharField(max_length=50)
    price = models.FloatField()
    stock = models.IntegerField()
    image = models.CharfieldField(max_length=1000)

class Product(models.Model):
    name = models.CharField(max_length=50)
    price = models.FloatField()
    stock = models.IntegerField()
    image = models.FileField()

Après avoir changé la propriété dans le model, il faut faire une migration

python manage.py makemigrations products  #le nom de l'app
python manage.py migrate

Ensuite il faut faire quelques modification de code, dans le settings.py de votre projet :

#monprojet\settings.py
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'

#monprojet\urls.py
from django.contrib import admin
from django.urls import path, include

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('products/', include('products.urls'))
]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Introduction à Jupyter Notebook

Jupyter Notebook est une interface web pour faire de la datascience, vous exécutez vos scnippet Python dans ce qu’on appelle des cells, des unités d’exécution.

Pour l’installer, voici la procédure :

Installation des librairies

pip install pandas
pip install numpy
pip install matplotlib
pip install xlrd

Installation de Jupyter Notebook

pip install notebook

#démarrer notebook
jupyter notebook

Vous pouvez installer d’abord Jupyter Notebook et installer les librairies depuis Jupyter Notebook

!pip install pandas 

le ! permet d'accéder au shell.

Afficher les log server dans Python Django

Il est pratique de logger en mode développement les requêtes servies par le serveur de Python

dans le fichier settings.py de votre projet mettez ce code juste après le DEBUG = true

if DEBUG:
    # will output to your console
    logging.basicConfig(
        level = logging.DEBUG,
        format = '%(asctime)s %(levelname)s %(message)s',
    )
else:
    # will output to logging file
    logging.basicConfig(
        level = logging.DEBUG,
        format = '%(asctime)s %(levelname)s %(message)s',
        filename = '/my_log_file.log',
        filemode = 'a'
    )

https://stackoverflow.com/questions/4558879/python-django-log-to-console-under-runserver-log-to-file-under-apache

Comprendre le mot clé with en Python

La syntaxe en Python de with est assez étrange, quel problème essait-il de résoudre?

with est utilisé lorsqu’on travaille avec des ressources non managées, notamment ouverture d’une fichier. Avant on utilisait try...catch,

    set things up
    try:
        do something
    finally:
        tear things down

Syntaxe basique de with

with expression [as variable]:
    with-block

L’expression est évaluée, et retourne un objet qui implémente le context management protocol, c’est à dire qui possède les méthodes __enter__() et __exit__()

with open('text.txt','w') as fichier
    fichier.write('Hello')

Avec cette syntaxe pas besoin de fermer explicitement le fichier, c’est automatique.

Retour en haut