Modules et packages Python
Comprendre les modules Python, leur mécanisme d'importation (syntaxe d'importation, chemins de recherche) et leurs attributs. Comprendre les packages Python, apprendre à créer des distributions de packages et à les mettre à disposition d'autres utilisateurs.
Modules Python
- Un module Python est simplement un conteneur pour les fonctions et les classes.
- Tout fichier Python '.py' peut être considéré comme un module
- Voici un exemple de module Python contenant des fonctions :
# File: greetings.py
def hello_en(name: str) -> None:
"""Say Hello in English"""
print(f"Hello {name}!")
def hello_fr(name: str) -> None:
"""Say Hello in French"""
print(f"Bonjour {name}!")
-
Le fichier « greetings.py » représente un module nommé « greetings » (nom du fichier module sans l'extension « .py ») contenant les fonctions « hello_en » et « hello_fr ».
-
Voici comment nous utilisons les fonctions fournies par le module « greetings » depuis un autre fichier (ou module) Python :
# File: main.py
# First form
import greetings
greetings.hello_fr("hackerstack.org")
greetings.hello_en("hackerstack.org")
# Second form
from greetings import *
hello_fr("hackerstack.org")
hello_en("hackerstack.org")
# Third form
from greetings import hello_fr, hello_en
hello_fr("hackerstack.org")
hello_en("hackerstack.org")
# Bonus: you can also rename functions or classes
# that you import from a module using aliases
from greetings import hello_fr as hello
hello("gmkziz")
-
Les différentes syntaxes d'importation présentées ci-dessus sont équivalentes en termes de performances, car les modules utilisés lors des importations seront entièrement chargés.
-
Pour accélérer le chargement des modules lors des exécutions, Python les précompile systématiquement lors de leur première exécution.
-
Pour nos exemples avec le module « greetings », Python a créé un répertoire « __pycache__ » pour stocker une version compilée en bytecode du module :
$ file __pycache__/greetings.cpython-312.pyc
__pycache__/greetings.cpython-312.pyc: Byte-compiled Python module for CPython 3.12 or newer, timestamp-based, .py timestamp: ..., .py size: 186 bytes
-
La version du module compilée en bytecode (.pyc) sera recompilée lors de sa prochaine exécution si son contenu a été modifié.
-
Si vous souhaitez effectuer une action lors de l'importation d'un module, il vous suffit d'ajouter la tâche dans le fichier « .py » du module. Voici un exemple :
# File: greetings.py
def hello_en(name: str) -> None:
"""Say Hello in English"""
print(f"Hello {name}!")
# This will run during module import
print(f"Module {__name__} loaded!")
# File: main.py
from greetings import hello_en
hello_en("Hackerstack!")
# Run main.py
$ python main.py
Module greetings loaded!
Hello Hackerstack!!
# Run greetings.py
$ python greetings.py
Module __main__ loaded!
-
Nous avons ajouté une nouvelle ligne dans le fichier 'greetings.py' pour afficher le nom du module à l'aide de la variable intégrée '__name__' de Python.
-
Désormais, lorsqu'on exécute un script important le module « greetings », le message « Module greetings chargé ! » s'affiche. La valeur de la variable « __name__ » correspond au nom du module importé.
-
Mais, lorsque nous exécutons le fichier du module lui-même (« greetings.py »), le message « Module __main__ chargé ! » s'affiche. La valeur de la variable « __name__ » correspond à « __name__ ».
-
C'est pourquoi vous verrez dans certains codes Python utilisés pour le scripting, quelque chose comme ceci :
# File: greetings.py
"""
Say hello in many languages. Awesome!
"""
# Using this file for scripting, which means we will
# run greetings.py directly to accomplish specific tasks
def hello_en(name: str) -> None:
"""Say Hello in English"""
print(f"Hello {name}!")
# The condition below simply means:
# if you run this module directly (not through imports)
if __name__ == '__main__':
hello_en("gmkziz")
# Run
$ python greetings.py
Hello gmkziz!
- Python recherche d'abord les modules importés dans le répertoire courant. S'ils ne les trouvent pas, il les recherche ensuite dans l'ordre, aux emplacements indiqués par la fonction `sys.path`. Voici les chemins d'accès sur ma configuration. Ils ne seront pas forcément identiques pour vous :
# Open Python interpreter and run sys.path
$ python
>>> import sys
>>> sys.path
['', '/usr/lib/python312.zip', '/usr/lib/python3.12', '/usr/lib/python3.12/lib-dynload', '/usr/local/lib/python3.12/dist-packages', '/usr/lib/python3/dist-packages']
- Vous pouvez utiliser la fonction « dir » pour lister tous les composants d'un module (ou les attributs de l'objet module), comme indiqué ci-dessous :
# From the directory where the greetings.py file resides
$ python
>>> import greetings
>>> dir(greetings)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'hello_en']
- Les noms des attributs intégrés disponibles pour tous les modules Python commencent et se terminent par deux tirets bas (_). Voici des exemples de résultats pour certains de ces attributs intégrés :
# Path to the source file of the module
>>> greetings.__file__
'/home/gmkziz/code/python/greetings.py'
# Name of the module
>>> greetings.__name__
'greetings'
# Path to the Byte-compiled file of the module.
# Used to speed up imports
>>> greetings.__cached__
'/home/gmkziz/code/python/__pycache__/greetings.cpython-312.pyc'
# Documentation of the module
>>> greetings.__doc__
'\nSay hello in many languages. Awesome!\n'
# Name of the package in which the module is included
# Empty because the greetings module is not include in a package
# More on Python packages next.
>>> greetings.__package__
Paquets Python
-
Un package en Python est simplement un conteneur pour les modules Python (un répertoire contenant les fichiers '.py' des modules).
-
Le nom du répertoire correspondra au nom du paquet et un fichier « __init__.py » devra également être créé dans ce répertoire pour indiquer à Python qu'il s'agit d'un paquet.
-
Créons un paquetage appelé « langue » contenant les modules « fr » et « en » que nous utiliserons pour dire bonjour en anglais et en français :
├── language
│ ├── en.py
│ ├── fr.py
│ └── __init__.py
├── main.py
- Voici le contenu des modules « en » et « fr » inclus dans le package « language », ainsi que le contenu du fichier main.py :
# File: en.py
def say_hello():
"""Say hello in English"""
print("Hello")
# File: fr.py
def say_hello():
"""Say hello in French"""
print("Salut")
- Voici le contenu du fichier « main.py » et le résultat après son exécution :
# File: main.py
from language import fr, en
en.say_hello()
fr.say_hello()
print(f"The en module is included in the package named {en.__package__}")
print(f"The source file of the en module is located at {en.__file__}")
# Result after running main.py
$ python main.py
Hello
Salut
The en module is included in the package named language
The source file of the en module is located at /home/gmkziz/code/python/language/en.py
-
Une fois votre package prêt, vous pouvez créer un package de distribution source (sdist) et/ou un package de distribution binaire (bdist) afin de le partager.
-
Le package de distribution source est une archive du code source et la distribution binaire utilise le format binaire du package Python appelé roue (extension '.whl')
-
Également roue L'outil en ligne de commande peut être utilisé pour manipuler les fichiers wheel (décompresser, recompresser, etc.).
-
Outils de configuration est l'un des l' construire des backends vous pouvez l'utiliser pour créer des packages Python.
-
La méthode recommandée aujourd'hui pour créer des packages Python est la suivante :
-
déclarer un construire le backend comme setuptools à l'intérieur d'un pyproject.toml fichier de configuration
-
Déclarez votre configuration d'empaquetage et utilisez l'utilitaire de construction approprié pour créer les distributions des sources et des binaires des paquets. Si vous utilisez setuptools comme moteur de construction, par exemple, vous utiliserez build
-
-
La routine shell pyproject.toml Le fichier de configuration devient la norme pour la gestion des projets Python. Il est utilisé par les outils populaires de gestion des dépendances et de packaging des projets Python, tels que… Poïétie et uv
-
Ensuite, nous allons créer une distribution source et binaire pour notre package « langage » en utilisant un fichier « pyproject.toml » et setuptools comme moteur de construction.
-
Pour cela, nous devons d'abord nous assurer que build L'utilitaire est installé :
pip install --upgrade build
- Nous pouvons maintenant créer le pyproject.toml fichier de configuration:
# File: pyproject.toml
[project]
name = "language"
description = "My super language package"
version = "0.0.1"
readme = "README.md"
authors = [
{name = "gmkziz", email = "gmkziz@hackerstack.org"}
]
license-files = ["LICEN[CS]E*", "vendored/licenses/*.txt", "AUTHORS.md"]
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
- Il est également recommandé d'ajouter un fichier « README.md » et un fichier « LICENCE » à la racine de votre projet. Voici l'arborescence finale des répertoires avant la création du package :
.
└── language
├── language
│ ├── en.py
│ ├── fr.py
│ └── __init__.py
├── LICENCE
├── pyproject.toml
└── README.md
- Pour créer les distributions source et binaire du paquet, il suffit maintenant d'exécuter la commande suivante :
$ python -m build
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- setuptools
* Getting build dependencies for sdist...
(...)
adding 'language/__init__.py'
adding 'language/en.py'
adding 'language/fr.py'
adding 'language-0.0.1.dist-info/licenses/LICENCE'
adding 'language-0.0.1.dist-info/METADATA'
adding 'language-0.0.1.dist-info/WHEEL'
adding 'language-0.0.1.dist-info/top_level.txt'
adding 'language-0.0.1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built language-0.0.1.tar.gz and language-0.0.1-py3-none-any.whl
- Les paquets résultants sont disponibles dans le répertoire « dist » :
$ tree language/dist/
language/dist/
├── language-0.0.1-py3-none-any.whl
└── language-0.0.1.tar.gz
-
Vous pouvez désormais partager les fichiers de distribution publiquement. Index des packages Python (PyPI) Vous pouvez les mettre à disposition dans un dépôt PyPI privé ou ailleurs.
-
Publier votre colis au public PyPI Le dépôt permettra de l'installer directement via pépin
-
Vous pouvez publier votre paquet sur PyPI depuis la ligne de commande en utilisant ficelle
-
Lorsque vous partagez directement le fichier de distribution binaire du package, les utilisateurs peuvent installer votre package comme suit :
# Is there any package whose name starts
# with language already installed ? No
$ pip freeze | egrep ^language
# Install the language package
$ cd dist && pip install language-0.0.1-py3-none-any.whl
Defaulting to user installation because normal site-packages is not writeable
Processing ./language-0.0.1-py3-none-any.whl
Installing collected packages: language
Successfully installed language-0.0.1
# Is there any package whose name starts
# with language already installed ? Yes
$ pip freeze | egrep ^language
language @ file:///home/gmkziz/code/python/language/dist/language-0.0.1-py3-none-any.whl#sha256=61c5e9414e30d0ff6e212bd71fa1d6b812f2e3c17717cc438e2d4da405b9c929
- Le module de langage est désormais inclus dans mon installation Python. Je peux l'importer depuis n'importe quel module Python. Voici un exemple d'utilisation depuis l'interpréteur Python :
# Run Python interpreter from a random directory
$ python
>>> from language import fr
>>> fr.say_hello()
Salut
>>> # The package has been loaded from here
>>> language.__path__
['/home/gmkziz/.local/lib/python3.12/site-packages/language']
Voilà, c'est tout. J'espère que vous comprenez mieux les modules et les packages Python maintenant.
Vous souhaitez signaler une erreur ou poser une question ? N'hésitez pas à m'envoyer un e-mail à gmkziz@hackerstack.org.
Si vous aimez mes articles, pensez à vous inscrire à ma newsletter afin de recevoir les derniers articles dès qu'ils sont disponibles.
Prenez soin de vous, continuez à apprendre et à bientôt pour le prochain post 🚀