Import external libraries and organize your code into functional chunks. For interactive reading and executing code blocks and find b05-pypckg.ipynb, or install Python and JupyterLab locally.
Watch this section as a video
Watch this section as a video on the @Hydro-Morphodynamics channel on YouTube.
Importer des paquets ou des modules¶
L’importation d’un paquet ou d’un module dans Python rend les fonctions externes et d’autres éléments (tels que les objets) des modules accessibles dans un script. Les fonctions et autres éléments sont stockés dans un autre fichier Python (.py) dans le dossier site-packages (répertoire) de l’environnement de l’interprète. Ainsi, pour utiliser un paquet non standard, il doit d’abord être téléchargé et installé. Les paquets Python standard (par exemple, os, math) sont toujours accessibles, et d’autres peuvent être ajoutés avec conda ou pip (read more pip-installing).
Le paquet os fournit des commandes de base de type système-terminal, par exemple, pour gérer les répertoires de dossiers. Importons donc ce paquet essentiel :
import os
print(os.getcwd()) # print current working directory
print(os.path.abspath('')) # print directory of script running/home/schwindt/github/hyhome-v2/jupyter
/home/schwindt/github/hyhome-v2/jupyter
Aperçu des options d’importation¶
Voici un aperçu des options pour importer des paquets ou des modules (parties hiérarchiques des paquets):
| Command | Description | Usage of attributes |
|---|---|---|
import package-name | Import an original module | package.item() |
import package-name as nick-name | Import module and rename (alias) it in the script | nick-name.item() |
from package-name import item | Import only a function, class or other items | item() |
from package-name import * | Import all items | item() |
Exemple¶
import matplotlib.pyplot as plt # import the pyplot module of the matplotlib package and alias it with plt
x = []
y = []
for e in range(1, 10):
x.append(e)
y.append(e**2)
plt.plot(x, y)Quelle est la meilleure façon d’importer un paquet ou un module?¶
Il n’y a pas de réponse globale à cette question. Cependant, sachez que from package-name import * écrase toute variable existante ou tout autre élément du script. Ainsi, n’utilisez * que lorsque vous connaissez tout le contenu d’un module ou d’un paquet. C’est aussi la raison pour laquelle PEP 8 décourage les importations de wildcard et recommande de placer toutes les importations en haut d’un script. La déclaration d’importation au milieu de l’exemple suivant est uniquement à des fins de démonstration:
pi = 9.112 # define a float called pi
print(f"Pi is not {pi:.3f}.")
from math import pi # this overwrites the previously defined variable pi
print(f"Pi is {pi:.3f}.")Quels sont les éléments (attributs, classes, fonctions) dans un module?¶
Parfois, nous voulons explorer des modules ou vérifier des attributs variables. Ceci est réalisé avec la commande dir():
import sys
print(sys.path)
print(dir(sys.path))
a_string = "zabaglione"
print(", ".join(dir(a_string)))['/home/schwindt/miniforge3/envs/wrr-proj/lib/python311.zip', '/home/schwindt/miniforge3/envs/wrr-proj/lib/python3.11', '/home/schwindt/miniforge3/envs/wrr-proj/lib/python3.11/lib-dynload', '', '/home/schwindt/miniforge3/envs/wrr-proj/lib/python3.11/site-packages']
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
__add__, __class__, __contains__, __delattr__, __dir__, __doc__, __eq__, __format__, __ge__, __getattribute__, __getitem__, __getnewargs__, __getstate__, __gt__, __hash__, __init__, __init_subclass__, __iter__, __le__, __len__, __lt__, __mod__, __mul__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __rmod__, __rmul__, __setattr__, __sizeof__, __str__, __subclasshook__, capitalize, casefold, center, count, encode, endswith, expandtabs, find, format, format_map, index, isalnum, isalpha, isascii, isdecimal, isdigit, isidentifier, islower, isnumeric, isprintable, isspace, istitle, isupper, join, ljust, lower, lstrip, maketrans, partition, removeprefix, removesuffix, replace, rfind, rindex, rjust, rpartition, rsplit, rstrip, split, splitlines, startswith, strip, swapcase, title, translate, upper, zfill
Créer un nouveau module¶
Dans la programmation orientée objet et la factorisation de code, l’écriture personnalisée, de nouveaux modules est une tâche essentielle. Pour écrire un nouveau module, créez d’abord un nouveau script. Ensuite, ouvrez le nouveau script et ajoutez quelques paramètres et fonctions.
# icecreamdialogue.py
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = f"Hi, I only have {flavors[0]}. How many scoops do you want?"icecreamdialogue.py can now either be executed as a script (nothing will happen visibly) or imported as a module to access its variables (e.g., icecreamdialogue.flavors):
import icecreamdialogue as icd
print(icd.welcome_msg)
scoops_wanted = 2
print(f"That makes {icd.price_scoops[scoops_wanted]} please")Faites du script autonome¶
As an alternative, we can append the call to items in icecreamdialogue.py in the script and run it as a stand-alone script by adding an if __name__ == "__main__": block:
# icecreamdialogue_standalone.py
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = f"Hi, I only have {flavors[0]}. How many scoops do you want?"
if __name__ == "__main__":
print(welcome_msg)
scoops_wanted = 2
print(f"That makes {price_scoops[scoops_wanted]} please")Now we can run icecreamdialogue_standalone.py in a terminal (e.g., Linux Terminal, PyCharm’s Terminal tab at the bottom of the window, or VS Code’s integrated terminal).
C:\temp\ python icecreamdialogue_standalone.pyScripts autonomes avec paramètres d’entrée¶
Pour rendre le script plus flexible, nous pouvons définir, par exemple, scoops_wanted comme variable d’entrée d’une fonction.
# icecreamdialogue_standalone_withinput.py
import sys # sys provides access to command line arguments
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = f"Hi, I only have {flavors[0]}. How many scoops do you want?"
def dialogue(scoops_wanted): # formerly in the __main__ statement
print(welcome_msg)
print(f"That makes {price_scoops[scoops_wanted]} please")
if __name__ == "__main__":
if len(sys.argv) > 1: # make sure input is provided
# if true: call the dialogue function with the input argument
dialogue(int(sys.argv[1]))Maintenant, nous pouvons lancer icecreamdialogue_standalone_withinput.py dans un terminal.
C:\temp\ python icecreamdialogue_standalone_withinput.py 2Initialisation d’un paquet (module organisé de façon hiérarchisée)¶
La bonne pratique implique qu’un script ne dépasse pas 50 à 100 lignes de code (sauf les documents en ligne et les variables multilignes). Par conséquent, un paquet sera probablement constitué de plusieurs scripts qui sont stockés dans un dossier et un script de base sert à l’initiation des scripts. Ce script principal s’appelle __init__.py et Python invoquera toujours ce nom de script dans un dossier de paquetage. Exemple de structure d’un paquet appelé icecreamery:
icecreamery(nom du dossier)__init__.py- initiation du paquet * script Python*icecreamdialogue.py- dialogue produisant le script Pythonicecream_maker.py- production de crème glacée virtuelle * script Python*
Pour invoquer automatiquement les deux scripts pertinents (sous-modules) du paquet icecreamery, le __init__.py doit inclure les éléments suivants :
# __init__.py
print(f'Invoking __init__.py for {__name__}') # only for demonstration - keep __init__.py silent in production packages
import icecreamery.icecreamdialogue, icecreamery.icecream_maker# example usage of the icecreamery package
import icecreamery
print(icecreamery.icecreamdialogue.welcome_msg)Vous souvenez-vous de la fonction dir()? Appliquée à un paquet (par exemple, dir(icecreamery)), elle énumère les éléments actuellement définis dans l’espace de noms du paquet. Cependant, pour contrôler les sous-modules qu’une importation de joker (from icecreamery import *) charge, définissez une liste __all__ dans la liste __init__.py:
# __init__.py with __all__ list
__all__ = ['icecreamdialogue', 'icecream_maker']L’exemple complet du paquet icecreamery_all est également disponible dans un dépôt icecream.
# example usage of the icecreamery package
from icecreamery_all import *
print(icecreamdialogue.welcome_msg)Sommaire de la création du paquet¶
Un paquet hiérarchiquement organisé contient un fichier __init__.py avec une liste __all__ pour invoquer les scripts de module pertinents. La structure d’un module peut être plus complexe que la liste d’exemples ci-dessus (par exemple, avec des sous-dossiers). Lorsque vous écrivez un paquet, pensez à utiliser meaningful script and variable names, avec la documentation appropriée.
Recharger (réimporter) un paquet ou un module¶
Depuis Python 3, recharger un module nécessite d’importer le module importlib. Recharger n’a de sens que si vous écrivez activement un nouveau module. Pour recharger un module, tapez :
import importlib
importlib.reload(my_module)Développement de paquets et déploiement PyPI (pip)¶
L’exemple icecreamery montre comment un paquet fonctionne en interne. Pour qu’un paquet puisse être installé par l’intermédiaire de pip install icecreamery, il doit être déployé à PyPI, l’index du paquet Python que pip interroge en arrière-plan (recall pip-installing). Cette section résume d’abord le flux de travail de déploiement, y compris l’automatisation avec les flux de travail GitHub et la documentation sur Read the Docs, et explique ensuite les bonnes pratiques pour développer un paquet en collaboration.
Du code local à un paquet installable par pip¶
L’emballage Python moderne est animé par un seul fichier pyproject.toml, qui remplace l’ancien fichier setup.py (voir PEP 621). Un dépôt prêt au déploiement ressemble à la structure suivante, connue sous le nom de mise en page src :
icecreamery/ (repository root)
src/
icecreamery/ (the package itself)
__init__.py
icecreamdialogue.py
icecream_maker.py
tests/ (automated tests, e.g., for pytest)
docs/ (documentation source, e.g., for Sphinx)
examples/ (functional usage examples)
pyproject.toml (package metadata and build configuration)
README.md
LICENSELe fichier pyproject.toml définit comment pip (ou tout autre installateur) construit et installe le paquet:
[build-system]
requires = ["setuptools>=77"]
build-backend = "setuptools.build_meta"
[project]
name = "icecreamery"
version = "0.1.0"
description = "Virtual ice cream sales dialogues"
readme = "README.md"
license = "BSD-3-Clause"
requires-python = ">=3.10"
dependencies = [
"matplotlib",
"numpy",
]Lors du développement, installer le paquet en mode editable dans l’environnement actif:
pip install -e .Le drapeau -e (éditable) fait importer le paquet directement à partir du clone de développement local au lieu d’une copie statique dans le dossier site-packages, de sorte que les modifications de code prennent effet immédiatement sans ré-installer.
Pour déployer manuellement une version sur PyPI :
Inscrivez-vous (gratuitement) à pypi.org et, pour répéter les téléchargements, à test.pypi.org.
Construire les archives de distribution (une archive source et une roue) avec
python -m build(installer le constructeur une fois avecpip install build). Les archives atterrissent dans un nouveau dossierdist/.Téléchargez les archives avec twine:
python -m twine upload dist/*. Meilleure pratique : répéter le téléchargement avecpython -m twine upload --repository testpypi dist/*d’abord.
C’est fait. Désormais, tout le monde peut pip install icecreamery.
Automatiser les essais et le déploiement avec GitHub Workflows¶
La construction manuelle et le téléchargement de chaque version sont sujets aux erreurs. GitHub Actions automatiser ces tâches récurrentes avec des workflows, qui sont des fichiers YAML stockés dans le dossier .github/workflows/ d’un dépôt. Deux flux de travail sont particulièrement utiles pour le développement de paquets :
Un workflow test (intégration continue) qui exécute la suite de test (p. ex., avec pytest) pour chaque requête push et pull, idéalement sur plusieurs versions Python et systèmes d’exploitation. Ainsi, le code cassé est signalé avant qu’il ne soit fusionné en
main(appelez la section Collaboration & Branches).Un flux de travail publié qui construit et télécharge le paquet vers PyPI chaque fois qu’une nouvelle version (étiquette de version) est publiée sur GitHub.
La meilleure pratique pour le workflow de publication est PyPI Trusted Publishing, qui relie le dépôt GitHub directement au projet PyPI (une configuration unique dans les paramètres du compte PyPI), de sorte qu’aucun jeton API ne doit être stocké dans les secrets du dépôt:
# .github/workflows/publish.yml
name: Publish to PyPI
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # required for PyPI Trusted Publishing
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build distribution archives
run: |
python -m pip install build
python -m build
- name: Upload to PyPI
uses: pypa/gh-action-pypi-publish@release/v1Avec ce workflow en place, publier une nouvelle version réduit à augmenter le numéro de version à pyproject.toml et en cliquant sur Projeter une nouvelle version (avec une balise comme v0.1.1) sur GitHub.
Documentation sur la lecture des docs (plan gratuit)¶
Un paquet pip-installable sans documentation ne sera guère utilisé par personne. La norme de facto pour l’hébergement de la documentation du paquet Python est Lire le Docs, qui construit et héberge la documentation des dépôts publics (open-source) gratuitement à PACKAGE-NAME.readthedocs.io (le plan gratuit affiche les petites annonces). Le flux de travail :
Écrivez des docstrings cohérents (p. ex., en numpy ou Google style) pour tous les modules, classes et fonctions, afin que les générateurs de documentation puissent rendre la référence API automatiquement.
Créez un dossier
docs/avec un projet Sphinx (sphinx-quickstart) et activez les extensionssphinx.ext.autodocetsphinx.ext.napoleondansdocs/conf.pypour tirer les docstrings dans la documentation. MkDocs avec le plugin mkdocstrings est une alternative populaire.Ajouter un fichier de configuration
.readthedocs.yaml(requis par Read the Docs) à la racine du dépôt :
# .readthedocs.yaml
version: 2
build:
os: ubuntu-24.04
tools:
python: "3.12"
sphinx:
configuration: docs/conf.py
python:
install:
- method: pip
path: .
- requirements: docs/requirements.txtConnectez-vous à readthedocs.org avec un compte GitHub et importez le dépôt. Lire les Docs installe un webhook, donc chaque poussée à
maindéclenche une reconstruction automatique de la documentation, et chaque balise de libération peut être publié comme une compilation de documentation spécifique à la version.
C’est fait. La documentation se met à jour avec chaque poussée.
Développement d’un ensemble de collaboration¶
Dès que plusieurs développeurs (par exemple un groupe de recherche) poussent le code vers le même dépôt de paquets, travailler sur dedicated branches with pull requests n’est que la moitié de l’histoire. Les règles de bonnes pratiques suivantes maintiennent un paquet de plus en plus durable (cette liste découle d’une expérience douloureuse avec le code de recherche réel):
** Suivez strictement PEP 8:**
Naming conventions : veillez à ce que tous les noms de fichiers de script (module) et de variables suivent les bonnes pratiques, c’est-à-dire les noms
lowercase_with_underscorespour les modules, les fonctions et les variables,CamelCasepour les classes etUPPERCASEpour les constantes (rappel meaningful script and variable names).Module shadowing : ne jamais nommer un script ou un dossier interne après une bibliothèque ou un module installé (par exemple,
math.py,numpy.py, ou un dossier appelématplotlib/). Parce que Python recherche le propre répertoire du script avant le dossier site-packages, le fichier local est importé au lieu de la bibliothèque prévue, ce qui conduit à des échecs d’importation apparemment inexplicables. Les noms trop génériques, tels queplots.pyouplots/, sont risqués pour la même raison : ils entrent facilement en collision avec des modules tiers et se confondent avec des bibliothèques commematplotlib.pyplot.Docstrings : équipez chaque module et chaque fonction d’un docstring, afin que les collaborateurs (et les générateurs de documentation, voir ci-dessus) comprennent ce que le code fait sans l’inverser.
Longueur du fichier et redondance du code: une structure modulaire signifie que les scripts doivent rester concis. Pour le contexte, nous avons dû une fois refactorer un script de tracé qui avait augmenté à plus de 3500 lignes, en partie à cause de blocs de code copiés (redondants). Découpez les grands fichiers en sous-modules logiques et suivez strictement le principe DRY (Ne répétez pas vous-même).
Utilisez des dossiers dédiés pour des exemples et des modèles pour garder le paquet de base propre (c.-à-d. le répertoire src/) propre :
dev-examples/: cas de recherche ou de développement en cours (p. ex.,dev-examples/cylinder-flume-telemac/). Configurer le dépôt pour bloquer les grands fichiers (p. ex., tout ce qui dépasse 20 Mo), car les grandes sorties de simulation n’appartiennent pas au dépôt git et doivent être sauvegardées séparément.examples/: des exemples finalisés, nettoyés et fonctionnels, ainsi que des informations README sur la façon de les exécuter.templates/: versions généralisées des scripts d’exemple qui utilisent des arguments de mots-clés au lieu de chemins codés en dur.
Maintenir la propreté stricte du haut niveau: ne pas ajouter ou déplacer de fichiers directement dans la racine du dépôt ou le répertoire source du paquet. Par exemple, gardez les scripts d’activation d’environnement dans un dossier dédié (par exemple env-scripts/) et invoquez-les à partir de votre environnement local ou de répertoires d’exemples au besoin.
Restez synchronisés avec main: tirez la dernière main branche régulièrement et toujours avant de créer une nouvelle branche. En outre, installer le paquet à partir du clone de développement local en mode modifiable (pip install -e .) au lieu de manipuler les importations relatives ou sys.path de sorte que les scripts d’exemple importent votre dernier code local plutôt qu’une copie site-packages installée au niveau mondial. Les assistants de l’IA (p. ex. Claude Code ou Codex) peuvent aider à refactorer solidement les scripts hérités avec des importations cassées, mais examiner leurs modifications aussi critiques que toute autre demande de tirage.
Vérification de la réussite en apprentissage¶
Prenez le test de réussite d’apprentissage pour ce carnet Jupyter.
Unfold QR Code
