Das `io`-Modul in Python

Das `io`-Modul in Python

Dieser Artikel erklärt das io-Modul in Python.

Wir erklären das io-Modul in Python mit praktischen Beispielen.

YouTube Video

Das io-Modul in Python

Die Eingabe-/Ausgabeverarbeitung bildet die Grundlage für alle Arten von Datenoperationen, wie Dateien, Netzwerke und Standard-I/O. Pythons io-Modul stellt eine Reihe von abstrakten Klassen zur Verfügung, die diese Ein- und Ausgabeoperationen vereinheitlichen. Das Schlüsselkonzept zum Verständnis dieses Moduls ist die Idee eines "Streams" (Datenstroms).

Was ist ein Datenstrom?

Ein Stream ist ein abstrakter Datenfluss, der ein sequentielles und kontinuierliches Lesen und Schreiben von Daten ermöglicht.

Beim Lesen von Dateiinhalten Byte für Byte oder beim Senden und Empfangen von Daten über ein Netzwerk können all diese Vorgänge als Datenströme behandelt werden.

Durch die Abstraktion dieses Mechanismus können Dateien, Speicher und Netzwerke – verschiedene I/O-Quellen – mit gemeinsamen Operationen wie Lesen und Schreiben verarbeitet werden.

Das io-Modul von Python bietet ein einheitliches Interface für Streams und ermöglicht so die effiziente Verarbeitung von sowohl Text- als auch Binärdaten.

Grundstruktur des io-Moduls

Das io-Modul besitzt gemäß der Art der Streams eine dreischichtige Hierarchie.

  1. Rohe Schicht (RawIOBase)

    RawIOBase verarbeitet die unterste Ebene des Byte-I/O, wie Datei-Deskriptoren des Betriebssystems und Geräte.

  2. Puffer-Schicht (BufferedIOBase)

    BufferedIOBase stellt einen Zwischenspeicher (Buffer) zur Verfügung, um die I/O-Effizienz zu steigern. BufferedReader und BufferedWriter sind typische Beispiele.

  3. Text-Schicht (TextIOBase)

    TextIOBase wandelt Bytesequenzen in Strings um und übernimmt die Kodierung. Normalerweise wird beim Öffnen einer Datei mit der Funktion open() der TextIOWrapper aus dieser Schicht verwendet.

Dank dieser Struktur trennt das io-Modul Text- und Binär-I/O klar voneinander, während flexible Kombinationen möglich sind.

Grundstruktur des io-Moduls

RawIOBase verarbeitet die Datei-Deskriptoren des Betriebssystems auf der untersten Ebene, BufferedIOBase fügt einen Zwischenspeicher hinzu und die oberste Schicht TextIOBase übernimmt die Umwandlung in Strings.

1import io
2
3# Check the core base classes hierarchy
4print(io.IOBase.__subclasses__())
  • Dieser Code dient zur Überprüfung der Gruppe von abstrakten Klassen, die von IOBase erben. Sie sehen TextIOBase, BufferedIOBase und RawIOBase, was die hierarchische Struktur bestätigt.

io.IOBase: Die Basisklasse aller Klassen

IOBase ist die abstrakte Basisklasse für alle I/O-Objekte und definiert gemeinsame Methoden wie close(), flush() und seekable(). Sie wird selten direkt genutzt und üblicherweise über abgeleitete Klassen angesprochen.

1import io
2
3f = io.StringIO("data")
4print(f.seekable())   # True
5print(f.readable())   # True
6print(f.writable())   # True
7f.close()
  • Dieses Beispiel zeigt, dass die gemeinsamen Methoden von IOBase auch in höheren Klassen verwendet werden können. seekable() und readable() sind nützlich, um die Eigenschaften eines Streams zu prüfen.

io.RawIOBase: Die unterste Schicht

RawIOBase ist die Ebene, die dem Datei-Deskriptor des Betriebssystems am nächsten ist und verzichtet auf Pufferung. Die typische Implementierung ist FileIO, das byteweise liest und schreibt.

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 ist eine konkrete Implementierung von RawIOBase; alle Lese- und Schreibvorgänge erfolgen als bytes. Die Effizienz kann erhöht werden, indem man sie mit der oberen BufferedIOBase-Schicht kombiniert.

io.BufferedIOBase: Zwischenschicht (mit Pufferung)

BufferedIOBase ist eine Zwischenschicht, die Pufferung durchführt und damit den Festplattenzugriff effizienter macht. Die Hauptimplementierungen sind BufferedReader, BufferedWriter, BufferedRandom und 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'
  • In diesem Beispiel werden die über BufferedWriter geschriebenen Daten vorübergehend in einem Speicherpuffer abgelegt und erst beim Aufruf von flush() tatsächlich an die untere Schicht übergeben.

Beispiel für BufferedReader

BufferedReader ist ein nur lesbarer, gepufferter Datenstrom, der effizientes Lesen mit peek() und read() unterstützt.

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() schaut nur "in die Daten hinein" und bewegt den Zeiger nicht. Durch die Kombination mit read() kann man die Pufferung flexibel steuern.

io.TextIOBase: Nur-Text-Schicht

TextIOBase ist eine Abstraktionsschicht zur Verarbeitung von Zeichenfolgen, die intern Dekodierung und Kodierung durchführt. Eine typische Implementierungsklasse ist 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'))
  • In diesem Beispiel kodiert TextIOWrapper die Zeichenkette in UTF-8 und schreibt sie in den zugrunde liegenden Binär-Stream.

Beispiel für das Lesen mit TextIOWrapper

Beim Lesen erfolgt die Dekodierung automatisch.

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 dient als grundlegende Klasse für Text-I/O und bildet die Basis für fast alle Dateioperationen auf hoher Ebene.

io.StringIO: Text-Stream im Speicher

StringIO ist eine Klasse, mit der man Zeichenketten im Speicher so behandeln kann, als wären sie Dateien. Sie ist nützlich für I/O-Tests und zur Erzeugung temporärer Daten.

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 ermöglicht dateiähnliche Operationen ohne Festplattenzugriff und wird häufig in Unit-Tests eingesetzt.

io.BytesIO: Binär-Stream im Speicher

BytesIO ist eine im Speicher befindliche Datei-Klasse zur Verarbeitung von Bytesequenzen (bytes). Sie ist nützlich für Situationen wie Binärverarbeitung oder Datenkompression, in denen man keine Dateien verwenden möchte.

1import io
2
3buf = io.BytesIO()
4buf.write(b'\x01\x02\x03')
5buf.seek(0)
6print(list(buf.read()))  # [1, 2, 3]
  • BytesIO besitzt das gleiche Interface wie BufferedIOBase und kann als Ersatz für viele Datei-APIs eingesetzt werden.

Eigene Streams (Erstellung von Originalklassen)

Die Klassen im io-Modul sind erweiterbar, sodass Sie eigene Stream-Klassen erstellen können. Unten finden Sie ein Beispiel für eine TextIOBase-Unterklasse, die beim Schreiben alle Texte in Großbuchstaben umwandelt.

 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"
  • Solange Sie den Vertrag von TextIOBase einhalten, können Sie ein beliebiges eigenes Verhalten auf diese Art definieren. Auch die Erweiterung von Streams für spezielle Anwendungen wie Dateien oder Netzwerke ist einfach möglich.

Zusammenfassung

Das io-Modul organisiert die Ein- und Ausgabe in einer Hierarchie aus abstrakten und konkreten Klassen.

  • RawIOBase ist eine Klasse für Byte-I/O auf Betriebssystemebene.
  • BufferedIOBase ist eine Klasse, die eine effiziente Zwischenspeicherschicht bereitstellt.
  • TextIOBase ist eine Klasse, die das Lesen und Schreiben von Zeichenketten verwaltet.
  • StringIO und BytesIO sind Klassen, die Streams im Speicher bereitstellen.

Wenn Sie diese Klassen verstehen, können Sie die Funktionsweise des Ein-/Ausgabesystems von Python präzise erfassen und auf Dateioperationen, Netzwerkkommunikation und das Design von Test-Streams anwenden.

Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.

YouTube Video