Moduł `io` w Pythonie

Moduł `io` w Pythonie

W tym artykule omówiono moduł io w Pythonie.

Wyjaśnimy moduł io w Pythonie na praktycznych przykładach.

YouTube Video

Moduł io w Pythonie

Przetwarzanie wejścia/wyjścia stanowi podstawę wszelkich operacji na danych, takich jak pliki, sieci i standardowe I/O. Moduł io w Pythonie udostępnia zestaw klas abstrakcyjnych, które ujednolicają te operacje wejścia/wyjścia. Kluczową koncepcją do zrozumienia tego modułu jest pojęcie „strumienia”.

Czym jest strumień?

Strumień to abstrakcyjny przepływ do sekwencyjnego i ciągłego odczytu oraz zapisu danych.

Podczas odczytu treści pliku bajt po bajcie lub wysyłania i odbierania danych przez sieć, wszystkie te działania mogą być obsługiwane jako strumienie danych.

Abstrahując ten mechanizm, pliki, pamięć i sieci — różne źródła I/O — mogą być obsługiwane za pomocą wspólnych operacji, takich jak odczyt i zapis.

Moduł io w Pythonie zapewnia jednolity interfejs dla strumieni, umożliwiając wydajne przetwarzanie danych tekstowych i binarnych.

Podstawowa struktura modułu io

Moduł io posiada trójwarstwową hierarchię zgodnie z charakterem strumieni.

  1. Warstwa surowa (RawIOBase)

    RawIOBase obsługuje najniższy poziom operacji bajtowych, takich jak deskryptory plików systemu operacyjnego oraz urządzenia.

  2. Warstwa buforowana (BufferedIOBase)

    BufferedIOBase zapewnia bufor, aby zwiększyć wydajność operacji wejścia/wyjścia. BufferedReader i BufferedWriter to typowe przykłady.

  3. Warstwa tekstowa (TextIOBase)

    TextIOBase konwertuje sekwencje bajtów na ciągi znaków i obsługuje kodowanie. Zazwyczaj podczas otwierania pliku za pomocą funkcji open(), wykorzystywany jest TextIOWrapper z tej warstwy.

Dzięki tej strukturze moduł io wyraźnie rozdziela operacje wejścia/wyjścia tekstowego i binarnego, umożliwiając przy tym elastyczne łączenie ich.

Podstawowa struktura modułu io

RawIOBase zarządza deskryptorami plików na najniższym poziomie, powyżej znajduje się warstwa BufferedIOBase dodająca bufor, a na samej górze TextIOBase odpowiadająca za konwersje na ciągi znaków.

1import io
2
3# Check the core base classes hierarchy
4print(io.IOBase.__subclasses__())
  • Ten kod służy do sprawdzenia grupy klas abstrakcyjnych dziedziczących po IOBase. Można zauważyć TextIOBase, BufferedIOBase i RawIOBase, potwierdzając tym samym strukturę hierarchiczną.

io.IOBase: Klasa bazowa dla wszystkich

IOBase to abstrakcyjna klasa bazowa dla wszystkich obiektów wejścia/wyjścia, definiująca wspólne metody, takie jak close(), flush(), czy seekable(). Rzadko używa się jej bezpośrednio – najczęściej korzysta się z niej poprzez klasy pochodne.

1import io
2
3f = io.StringIO("data")
4print(f.seekable())   # True
5print(f.readable())   # True
6print(f.writable())   # True
7f.close()
  • Ten przykład pokazuje, że wspólne metody IOBase są również dostępne w klasach wyższego poziomu. seekable() i readable() są przydatne do sprawdzania właściwości strumienia.

io.RawIOBase: Warstwa najniższego poziomu

RawIOBase to warstwa najbliższa deskryptorom plików systemu operacyjnego i nie wykonuje buforowania. Typową implementacją jest FileIO, który odczytuje i zapisuje dane bajtami.

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 to konkretna implementacja RawIOBase; wszystkie operacje odczytu i zapisu odbywają się w postaci obiektów bytes. Wydajność można poprawić, łącząc tę warstwę z wyższą warstwą BufferedIOBase.

io.BufferedIOBase: Warstwa pośrednia (z buforowaniem)

BufferedIOBase to warstwa pośrednia, która buforuje dane, dzięki czemu dostęp do dysku jest bardziej wydajny. Główne implementacje to BufferedReader, BufferedWriter, BufferedRandom i 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'
  • W tym przykładzie dane zapisane przez BufferedWriter są tymczasowo przechowywane w buforze pamięci, a rzeczywisty zapis do niższej warstwy następuje po wywołaniu flush().

Przykład użycia BufferedReader

BufferedReader to buforowany strumień tylko do odczytu, który umożliwia wydajne czytanie za pomocą peek() i 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() jedynie podgląda dane bez przesuwania wskaźnika. Łącząc ją z read(), można elastycznie sterować buforowaniem.

io.TextIOBase: Warstwa tekstowa

TextIOBase jest warstwą abstrakcji do obsługi ciągów znaków, która wewnętrznie wykonuje dekodowanie i kodowanie. Typową klasą implementacyjną jest 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'))
  • W tym przykładzie TextIOWrapper koduje ciąg na UTF-8 i zapisuje go do podłożonego strumienia binarnego.

Przykład odczytu przy użyciu TextIOWrapper

Podczas odczytu dekodowanie odbywa się automatycznie.

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 pełni rolę podstawowej klasy do operacji wejścia/wyjścia tekstowego i stanowi bazę dla niemal wszystkich wysokopoziomowych operacji na plikach.

io.StringIO: Tekstowy strumień w pamięci

StringIO to klasa pozwalająca obsługiwać ciągi znaków w pamięci tak, jakby były plikami. Przydaje się do testowania I/O i generowania tymczasowych danych.

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 umożliwia operacje podobne do plikowych bez użycia dysku i jest szeroko wykorzystywany w testach jednostkowych.

io.BytesIO: Binarny strumień w pamięci

BytesIO to klasa pliku w pamięci do obsługi sekwencji bajtów (bytes). Przydaje się w sytuacjach, takich jak przetwarzanie binarne lub kompresja danych, gdy nie chcemy używać plików.

1import io
2
3buf = io.BytesIO()
4buf.write(b'\x01\x02\x03')
5buf.seek(0)
6print(list(buf.read()))  # [1, 2, 3]
  • BytesIO ma taki sam interfejs jak BufferedIOBase i może być używany jako zamiennik wielu API do obsługi plików.

Strumienie własne (tworzenie oryginalnych klas)

Klasy znajdujące się w module io są rozszerzalne i pozwalają na tworzenie własnych klas strumieni. Poniżej znajduje się przykład podklasy TextIOBase, która podczas zapisu zamienia cały tekst na wielkie litery.

 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"
  • Dopóki przestrzegasz kontraktu TextIOBase, możesz definiować dowolne niestandardowe zachowania w podobny sposób. Łatwo również rozszerzać strumienie dla konkretnych zastosowań, takich jak pliki czy sieci.

Podsumowanie

Moduł io porządkuje przetwarzanie wejścia/wyjścia w hierarchię klas abstrakcyjnych i konkretnych.

  • RawIOBase to klasa do bajtowych operacji na poziomie systemu operacyjnego.
  • BufferedIOBase to klasa zapewniająca efektywną warstwę buforowania.
  • TextIOBase to klasa zarządzająca odczytem i zapisem ciągów znaków.
  • StringIO i BytesIO to klasy oferujące strumienie w pamięci.

Zrozumienie tych klas pozwala precyzyjnie pojąć działanie systemu wejścia/wyjścia w Pythonie i stosować je do operacji na plikach, komunikacji sieciowej oraz projektowania strumieni testowych.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video