Сообщить об ошибке.

Паттерны использования io.FileIO() в Python

Содержание:

io.FileIO - это сырое файловое I/O над файловым дескриптором ОС. Практически то же, что open(..., buffering=0) в бинарном режиме. Это RawIOBase-наследник, поэтому все паттерны RawIOBase тут применимы, но для файлов есть свои интересные сценарии.

Ниже, основные паттерны использования io.FileIO() в Python.

Получить "сырой" файловый поток (без буферизации)

Типичный паттерн - явно сказать "хочу raw-уровень":

from io import FileIO

## то же самое, что open("data.bin", "rb", buffering=0)
raw = FileIO("data.bin", "rb")

buf = bytearray(4096)
while True:
    n = raw.readinto(buf)
    if not n:
        break
    chunk = memoryview(buf)[:n]
    # обрабатываем chunk без лишних копий

Зачем:

  • максимально предсказуемое поведение (каждый read => системный read),
  • удобно строить свои буферизаторы / фильтры поверх FileIO (BufferedReader, шифратор, фреймер и т.п.).

Использовать FileIO как "драйвер" под Buffered* и TextIOWrapper

Очень общий паттерн:

import io

raw = io.FileIO("log.txt", "ab")           # FileIO (RawIOBase)
buf = io.BufferedWriter(raw)               # BufferedIOBase
text = io.TextIOWrapper(buf, encoding="utf-8")

text.write("hello\n")
text.flush()

Чем это отличается от просто open("log.txt", "a", encoding="utf-8")?

  • вы контролируете слои:

    • можете подсунуть свою реализацию BufferedWriter,
    • декорировать raw (логирование, шифрование),
    • менять только один слой (например, текстовый).

Работа напрямую с файловым дескриптором (fd)

Крутой паттерн FileIO: оборачивание уже существующего fd (например, из os.open, os.dup, subprocess и т.п.).

import os
from io import FileIO

fd = os.open("data.bin", os.O_RDONLY)
f = FileIO(fd, mode="rb", closefd=True)  # FileIO управляет закрытием fd

data = f.read()
f.close()  # закроет и FileIO, и fd

Или наоборот - выдёргиваем fd, чтобы использовать на уровне ОС:

from io import FileIO
import os

f = FileIO("data.bin", "rb")
fd = f.fileno()
os.lseek(fd, 0, os.SEEK_SET)
## потом можно продолжать работать через f

Паттерны:

  • интеграция с API, которое даёт только fd;
  • передача файлов в C-библиотеки;
  • file descriptor passing через Unix-сокеты и т.п.

FileIO + low-level flags из os.open (эксклюзив, неблокирующий и т.д.)

Иногда надо открыть файл с флагами, которых нет у open(), например O_EXCL, O_DIRECT и т.п.

import os
from io import FileIO

flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
fd = os.open("unique.lock", flags, 0o600)
try:
    f = FileIO(fd, "wb", closefd=True)
    f.write(b"locked\n")
    f.flush()
    # держим файл открытым, пока нужен lock
finally:
    # Закроем при выходе
    f.close()

Паттерн:

  • делаем os.open(...) с нужным набором флагов;
  • оборачиваем в FileIO для удобства работы.

FileIO как backend для mmap

Для mmap.mmap() нужен файловый дескриптор. Паттерн:

import mmap
from io import FileIO

f = FileIO("data.bin", "r+b")  # FileIO даёт интерфейс и fd
mm = mmap.mmap(f.fileno(), 0)  # 0 => весь файл

## теперь mm - как bytearray поверх файла
print(mm[:10])
mm[0:4] = b"TEST"
mm.flush()
mm.close()
f.close()

Здесь FileIO - просто удобный способ:

  • открыть файл,
  • получить fd,
  • при этом иметь "на всякий" полноценный Python-объект потока.

FileIO в тестах - как RawIOBase для ваших буферизаторов/фильтров

Вы пишете свой BufferedIOBase/фильтр и хотите протестировать его "по-взрослому" на реальном файле, а не только на BytesIO. Тогда:

import io

raw = io.FileIO("test.bin", "w+b")  # читаемый и записываемый
buf = io.BufferedRandom(raw)        # и чтение, и запись

buf.write(b"hello")
buf.seek(0)
print(buf.read())                   # b'hello'

То есть FileIO здесь - эталонный низкий уровень, чтобы убедиться, что ваш код работает не только с in-memory объектами.

Использовать FileIO для zero-copy с readinto

Поскольку FileIO - RawIOBase, у него есть полноценный readinto. Паттерн для больших файлов:

from io import FileIO

with FileIO("big.bin", "rb") as f:
    buf = bytearray(1024 * 1024)  # 1 MB
    while True:
        n = f.readinto(buf)
        if not n:
            break
        chunk = memoryview(buf)[:n]
        # обрабатываем chunk без создания новых bytes

Плюс:

  • минимум аллокаций,
  • предсказуемое поведение (в отличие от многослойного буферизованного стека).

Чётко понимать: когда НЕ использовать FileIO

Чаще всего вам НЕ нужен FileIO, если:

  • достаточно обычного open("file.txt", "r", encoding="utf-8");
  • вам не нужен контроль над уровнями I/O;
  • не требуется fileno() или специальные флаги os.open.

В типичном прикладном коде паттерн проще:

with open("file.txt", "r", encoding="utf-8") as f:
    for line in f:
        ...

А вот когда:

  • строите свои слои I/O;
  • работаете с fd и системным I/O;
  • интегрируетесь с C/OS/low-level API -

тогда io.FileIO - правильный кирпичик.