Le module `io` en Python

Le module `io` en Python

Cet article explique le module io en Python.

Nous allons expliquer le module io en Python à l'aide d'exemples concrets.

YouTube Video

Le module io en Python

Le traitement des entrées/sorties constitue la base de toutes sortes d'opérations sur les données, telles que les fichiers, les réseaux et l'E/S standard. Le module io de Python fournit un ensemble de classes abstraites qui unifient ces opérations d'entrée/sortie. Le concept clé pour comprendre ce module est celui de "flux" (stream).

Qu'est-ce qu'un flux ?

Un flux est un flux abstrait pour lire et écrire des données de manière séquentielle et continue.

Lorsque l'on lit le contenu d'un fichier octet par octet ou que l'on envoie et reçoit des données sur un réseau, tout cela peut être traité comme des flux de données.

En généralisant ce mécanisme, les fichiers, la mémoire et les réseaux, c’est-à-dire des sources d’E/S différentes, peuvent être traités avec des opérations communes telles que la lecture et l’écriture.

Le module io de Python fournit une interface unifiée pour les flux, permettant une gestion efficace des données textuelles et binaires.

Structure de base du module io

Le module io dispose d'une hiérarchie en trois couches en fonction de la nature des flux.

  1. Couche brute (RawIOBase)

    RawIOBase gère les E/S octet par octet au niveau le plus bas, comme les descripteurs de fichiers du système d’exploitation et les périphériques.

  2. Couche tamponnée (BufferedIOBase)

    BufferedIOBase fournit un cache (tampon) pour améliorer l’efficacité des E/S. BufferedReader et BufferedWriter en sont des exemples typiques.

  3. Couche texte (TextIOBase)

    TextIOBase convertit des séquences d’octets en chaînes de caractères et gère le codage. Généralement, lorsqu’on ouvre un fichier avec la fonction open(), c’est TextIOWrapper de cette couche qui est utilisé.

Grâce à cette structure, le module io sépare clairement les E/S texte et binaire tout en permettant des combinaisons flexibles.

Structure de base du module io

RawIOBase gère les descripteurs de fichiers du système d’exploitation à la couche la plus basse, BufferedIOBase ajoute un cache au-dessus, et la couche supérieure TextIOBase s’occupe de la conversion des chaînes.

1import io
2
3# Check the core base classes hierarchy
4print(io.IOBase.__subclasses__())
  • Ce code permet de vérifier le groupe de classes abstraites qui héritent de IOBase. On peut voir TextIOBase, BufferedIOBase et RawIOBase, ce qui confirme la structure hiérarchique.

io.IOBase : La classe de base de toutes

IOBase est la classe de base abstraite pour tous les objets d’E/S, définissant les méthodes communes comme close(), flush() et seekable(). Elle est rarement utilisée directement et on y accède généralement via des classes dérivées.

1import io
2
3f = io.StringIO("data")
4print(f.seekable())   # True
5print(f.readable())   # True
6print(f.writable())   # True
7f.close()
  • Cet exemple montre que les méthodes communes de IOBase peuvent également être utilisées dans les classes supérieures. seekable() et readable() sont utiles pour vérifier les propriétés d’un flux.

io.RawIOBase : La couche la plus basse

RawIOBase est la couche la plus proche du descripteur de fichier de l’OS et n’effectue pas de mise en tampon. L’implémentation typique est FileIO, qui effectue la lecture et l’écriture octet par octet.

1import io, os
2
3# Create a low-level FileIO object (no buffering)
4fd = os.open('raw_demo.bin', os.O_RDWR | os.O_CREAT)
5raw = io.FileIO(fd, mode='w+')
6raw.write(b'abc123')
7raw.seek(0)
8print(raw.read(6))  # b'abc123'
9raw.close()
  • FileIO est une implémentation concrète de RawIOBase ; toutes les lectures et écritures sont effectuées en tant que bytes. L’efficacité peut être améliorée en le combinant avec la couche supérieure BufferedIOBase.

io.BufferedIOBase : Couche intermédiaire (avec tampon)

BufferedIOBase est une couche intermédiaire qui fait du tamponnement, rendant l’accès au disque plus efficace. Les principales implémentations sont BufferedReader, BufferedWriter, BufferedRandom et BufferedRWPair.

1import io
2
3# Create a buffered binary stream on top of a BytesIO (simulate file)
4base = io.BytesIO()
5buffered = io.BufferedWriter(base)
6buffered.write(b'Python IO buffering')
7buffered.flush()
8base.seek(0)
9print(base.read())  # b'Python IO buffering'
  • Dans cet exemple, les données écrites via BufferedWriter sont temporairement stockées dans un tampon en mémoire et sont effectivement transférées à la couche inférieure lors de l’appel à flush().

Exemple de BufferedReader

BufferedReader est un flux tamponné en lecture seule qui prend en charge une lecture efficace avec peek() et read().

1import io
2
3stream = io.BytesIO(b"1234567890")
4reader = io.BufferedReader(stream)
5print(reader.peek(5))   # b'12345' (non-destructive)
6print(reader.read(4))   # b'1234'
7print(reader.read(3))   # b'567'
  • peek() ne fait qu'observer (peek) les données et ne déplace pas le pointeur. En le combinant avec read(), on peut contrôler de manière flexible la mise en tampon.

io.TextIOBase : Couche texte seulement

TextIOBase est une couche d'abstraction pour la gestion des chaînes de caractères, effectuant en interne le décodage et l'encodage. Une classe d’implémentation typique est TextIOWrapper.

 1import io
 2
 3# Wrap a binary stream to handle text encoding
 4binary = io.BytesIO()
 5text_stream = io.TextIOWrapper(binary, encoding='utf-8')
 6text_stream.write("\u3053\u3093\u306B\u3061\u306F")
 7text_stream.flush()
 8
 9# Reset stream position
10binary.seek(0)
11
12# Read bytes once
13data = binary.read()
14
15# Show both raw bytes and decoded text
16print("Raw bytes:", data)
17print("Decoded text:", data.decode('utf-8'))
  • Dans cet exemple, TextIOWrapper encode la chaîne en UTF-8 et l’écrit dans le flux binaire sous-jacent.

Exemple de lecture avec TextIOWrapper

Le décodage est effectué automatiquement lors de la lecture.

1import io
2
3binary_data = io.BytesIO("Python I/O".encode('utf-8'))
4text_reader = io.TextIOWrapper(binary_data, encoding='utf-8')
5print(text_reader.read())  # 'Python I/O'
  • TextIOWrapper sert de classe fondamentale pour l’E/S texte et constitue la base de presque toutes les opérations de fichiers de haut niveau.

io.StringIO : Flux texte en mémoire

StringIO est une classe qui permet de traiter des chaînes de caractères en mémoire comme s’il s’agissait de fichiers. Elle est utile pour les tests d’E/S et la génération temporaire de données.

1import io
2
3text_buf = io.StringIO()
4text_buf.write("In-memory text stream")
5text_buf.seek(0)
6print(text_buf.read())  # 'In-memory text stream'
  • StringIO permet d’effectuer des opérations similaires à des fichiers sans utiliser le disque et est largement utilisé pour les tests unitaires.

io.BytesIO : Flux binaire en mémoire

BytesIO est une classe de fichier en mémoire pour manipuler des séquences d’octets (bytes). Elle est utile pour des situations telles que le traitement binaire ou la compression de données où l’on ne souhaite pas utiliser de fichiers.

1import io
2
3buf = io.BytesIO()
4buf.write(b'\x01\x02\x03')
5buf.seek(0)
6print(list(buf.read()))  # [1, 2, 3]
  • BytesIO possède la même interface que BufferedIOBase et peut être utilisé comme substitut à de nombreuses API de fichiers.

Flux personnalisés (création de classes originales)

Les classes du module io sont extensibles, ce qui permet de créer vos propres classes de flux. Voici ci-dessous un exemple de sous-classe TextIOBase qui met tout le texte en majuscules lors de l’écriture.

 1import io
 2
 3class UpperTextIO(io.TextIOBase):
 4    def __init__(self):
 5        self.buffer = ""
 6    def write(self, s):
 7        self.buffer += s.upper()
 8        return len(s)
 9
10u = UpperTextIO()
11u.write("hello io")
12print(u.buffer)  # "HELLO IO"
  • Tant que vous respectez le contrat de TextIOBase, vous pouvez définir n’importe quel comportement personnalisé comme celui-ci. Il est également facile d’étendre les flux pour des usages spécifiques, comme les fichiers et les réseaux.

Résumé

Le module io organise le traitement des entrées/sorties dans une hiérarchie de classes abstraites et concrètes.

  • RawIOBase est une classe pour les E/S octet par octet au niveau du système d’exploitation.
  • BufferedIOBase est une classe qui fournit une couche de cache efficace.
  • TextIOBase est une classe qui gère la lecture et l’écriture de chaînes de caractères.
  • StringIO et BytesIO sont des classes qui fournissent des flux en mémoire.

Comprendre ces classes vous permet de saisir précisément le fonctionnement du système d’E/S de Python et de les appliquer aux opérations sur les fichiers, à la communication réseau et à la conception de flux de test.

Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.

YouTube Video