Модуль `io` в Python

Модуль `io` в Python

В этой статье объясняется модуль io в Python.

Мы объясним модуль io в Python с практическими примерами.

YouTube Video

Модуль io в Python

Обработка ввода/вывода составляет основу всех видов работы с данными, таких как файлы, сети и стандартный ввод/вывод. Модуль io в Python предоставляет набор абстрактных классов, объединяющих эти операции ввода/вывода. Ключевое понятие для понимания этого модуля — это идея «потока».

Что такое поток?

Поток — это абстрактный поток для последовательного и непрерывного чтения и записи данных.

При чтении содержимого файла по байтам или при передаче и получении данных по сети все это может рассматриваться как потоки данных.

Благодаря абстрагированию этого механизма, файлы, память и сети — различные источники ввода/вывода — могут обрабатываться с помощью общих операций, таких как чтение и запись.

Модуль io в Python предоставляет унифицированный интерфейс для потоков, что позволяет эффективно работать как с текстовыми, так и с бинарными данными.

Базовая структура модуля io

Модуль io имеет трёхуровневую иерархию в зависимости от типа потоков.

  1. Низкоуровневый слой (RawIOBase)

    RawIOBase обрабатывает самый низкоуровневый байтовый ввод/вывод, такой как файловые дескрипторы ОС и устройства.

  2. Буферизованный слой (BufferedIOBase)

    BufferedIOBase предоставляет кэш (буфер) для повышения эффективности ввода/вывода. BufferedReader и BufferedWriter — типичные примеры.

  3. Текстовый слой (TextIOBase)

    TextIOBase преобразует последовательности байтов в строки и управляет кодировкой. Обычно при открытии файла функцией open() используется TextIOWrapper из этого слоя.

Благодаря этой структуре, модуль io чётко разделяет текстовый и бинарный ввод/вывод, позволяя гибко их комбинировать.

Базовая структура модуля io

RawIOBase работает с файловыми дескрипторами ОС на самом нижнем уровне, затем BufferedIOBase добавляет буферизацию, а верхний слой TextIOBase занимается преобразованием строк.

1import io
2
3# Check the core base classes hierarchy
4print(io.IOBase.__subclasses__())
  • Этот код проверяет группу абстрактных классов, наследуемых от IOBase. Вы можете увидеть TextIOBase, BufferedIOBase и RawIOBase, что подтверждает иерархическую структуру.

io.IOBase: Базовый класс для всех

IOBase — это абстрактный базовый класс для всех объектов ввода/вывода, определяющий общие методы, такие как close(), flush() и seekable(). Он редко используется напрямую и обычно применяется через производные классы.

1import io
2
3f = io.StringIO("data")
4print(f.seekable())   # True
5print(f.readable())   # True
6print(f.writable())   # True
7f.close()
  • Этот пример показывает, что общие методы IOBase также могут использоваться в производных классах. seekable() и readable() полезны для проверки свойств потока.

io.RawIOBase: Самый низкоуровневый слой

RawIOBase — это слой, ближайший к файловому дескриптору ОС и не выполняющий буферизацию. Типичная реализация — FileIO, который читает и записывает по байтам.

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 — конкретная реализация RawIOBase; все операции чтения и записи выполняются как объект bytes. Эффективность может быть повышена при объединении с верхним слоем BufferedIOBase.

io.BufferedIOBase: Промежуточный слой (с буферизацией)

BufferedIOBase — это промежуточный слой, который выполняет буферизацию, делая доступ к диску более эффективным. Основные реализации — это BufferedReader, BufferedWriter, BufferedRandom и 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'
  • В этом примере данные, записанные через BufferedWriter, временно сохраняются в буфере памяти и фактически передаются на нижний уровень после вызова flush().

Пример BufferedReader

BufferedReader — это поток с буферизацией только для чтения, который поддерживает эффективное чтение с помощью методов peek() и 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() только «подсматривает» данные и не перемещает указатель. Комбинируя его с методом read(), вы можете гибко управлять буферизацией.

io.TextIOBase: Слой только для текста

TextIOBase — это уровень абстракции для работы со строками, который внутренне выполняет декодирование и кодирование. Типичный класс-реализация — это 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'))
  • В этом примере TextIOWrapper кодирует строку в UTF-8 и записывает её во внутренний бинарный поток.

Пример чтения с использованием TextIOWrapper

Декодирование производится автоматически при чтении.

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 служит базовым классом для текстового ввода/вывода и является основой почти для всех операций с файлами на высоком уровне.

io.StringIO: Текстовый поток в памяти

StringIO — это класс, позволяющий работать со строками в памяти как с файлами. Он полезен для тестирования ввода/вывода и временного создания данных.

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 позволяет выполнять файловые операции без использования диска и широко используется в юнит-тестах.

io.BytesIO: Бинарный поток в памяти

BytesIO — это файловый класс в памяти для работы с последовательностями байтов (bytes). Он полезен для случаев, таких как обработка бинарных данных или сжатие данных, когда вы не хотите использовать файлы.

1import io
2
3buf = io.BytesIO()
4buf.write(b'\x01\x02\x03')
5buf.seek(0)
6print(list(buf.read()))  # [1, 2, 3]
  • BytesIO имеет тот же интерфейс, что и BufferedIOBase, и может использоваться как замена для многих файловых API.

Пользовательские потоки (создание собственных классов)

Классы в модуле io расширяемы, что позволяет создавать собственные классы потоков. Ниже приведён пример подкласса TextIOBase, который делает все символы заглавными при записи.

 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"
  • Пока вы соблюдаете контракт TextIOBase, вы можете определять любое подобное поведение. Также легко расширять потоки для конкретных целей, например работы с файлами и сетями.

Резюме

Модуль io организует обработку ввода/вывода в иерархию абстрактных и конкретных классов.

  • RawIOBase — это класс для байтового ввода/вывода на уровне ОС.
  • BufferedIOBase — это класс, обеспечивающий эффективный слой кэширования.
  • TextIOBase — это класс, управляющий чтением и записью строк.
  • StringIO и BytesIO — это классы, предоставляющие потоки в памяти.

Понимание этих классов позволяет точно понимать работу системы ввода/вывода Python и применять их для файловых операций, сетевого взаимодействия и проектирования тестовых потоков.

Вы можете следовать этой статье, используя Visual Studio Code на нашем YouTube-канале. Пожалуйста, также посмотрите наш YouTube-канал.

YouTube Video