Шаг 96.
Python: сборник рецептов. Файлы и ввод-вывод. Оборачивание существующего дескриптора файла для использования в качестве объекта файла

    На этом шаге мы рассмотрим способ решения этой задачи.

Задача

    У вас есть целочисленный файловый дескриптор, соответствующий уже открытому каналу ввода-вывода операционной системы (например, файлу, каналу, сокету и т. п.), и вы хотите обернуть его высокоуровневым объектом файла Python.

Решение

    Файловый дескриптор отличается от обычного открытого файла тем, что это просто целочисленный идентификатор, назначенный операционной системой, чтобы ссылаться на какой-то системный канал ввода-вывода. Если у вас есть дескриптор, вы можете обернуть его файловым объектом Python с помощью функции open(). Вы просто предоставляете целочисленный файловый дескриптор в качестве первого аргумента (вместо имени файла). Например:

#  Открыть низкоуровневый файловый дескриптор 
import os
fd = os.open('somefile.txt', os.O_WRONLY | os.O_CREAT)
#  Превратить в настоящий файл 
f = open(fd, 'wt') 
f.write('hello world\n') 
f.close()

    Когда высокоуровневый файловый объект закрывается или разрушается, его файловый дескриптор тоже будет закрыт. Если это нежелательное поведение, передайте необязательный аргумент closefd=False функции open(). Например:

#  Создать файловый объект, но не закрывать дескриптор по завершении 
f =open(fd, 'wt', closefd=False)
.   .   .   .


Обсуждение

    В Unix этот прием оборачивания файлового дескриптора может быть удобным способом для подключения файлоподобного интерфейса на существующий канал ввода-вывода, открытый другим способом (например, каналы, сокеты и т. д.). Вот пример с использованием сокетов: from socket import socket, AF_INET, SOCK_STREAM

def echo_dient(dient_sock, addr): 
    print('Got connection from', addr)

    #  Создать файловые обертки текстового режима для чтения/записи в сокет 
    dient_in = open(dient_sock.fileno(), 'rt', encoding='latin-1', dosefd=False)
    dient_out = open(dient_sock.fileno(), 'wt', encoding='latin-1', dosefd=False)

    #  Отправить клиенту, используя Echo-строки, применяя файловый ввод-вывод 
    for line in dient_in:
        dient_out.write(line)
        client_out.flush()
    dient_sock.dose()


def echo_server(address):
    sock = socket(AF_INET, SOCK_STREAM) 
    sock.bind(address) 
    sock.listen(1) 
    while True:
        client, addr = sock.accept() 
        echo_client(dient, addr)

    Важно подчеркнуть, что вышеприведенный пример нужен только для иллюстрации возможностей встроенной функции open(), и он работает kbim в Unix. Если вы пытаетесь применить файлоподобный интерфейс к сокету и хотите, чтобы ваш код был кроссплатформенным, используйте метод сокета makefile(). Однако если переносимость вас не беспокоит, вы можете обнаружить, что представленное выше решение обладает намного большей производительностью, нежели makefile().

    Вы также можете использовать этот прием для создания псевдонима, который позволит уже открытому файлу использоваться немного другим способом, нежели тот, каким он был изначально открыт. Например, вот так вы можете создать файловый объект, который позволит выводить бинарные данные в stdout (который по умолчанию открыт в текстовом режиме):

import sys

#  Создать файл в бинарном режиме для stdout
bstdout = open(sys.stdout.fileno(), 'wb', closefd=False)
bstdout.write(b'Hello World\n')
bstdout.flush()

    Хотя можно обернуть существующий файловый дескриптор и использовать его в качестве настоящего файла, обратите внимание, что не все файловые режимы могут поддерживаться и что некоторые типы файловых дескрипторов могут обладать забавными побочными эффектами (особенно по отношению к обработке ошибок, условиям достижения конца файла и т. д.). Это поведение также может изменяться в зависимости от операционной системы. В частности, ни один из приведенных примеров не будет работать за пределами Unix. Суть в том, что вам нужно тщательно тестировать свою реализацию, чтобы убедиться в том, что она работает так, как вы ожидаете.

    На следующем шаге мы рассмотрим создание временных файлов и каталогов.




Предыдущий шаг Содержание Следующий шаг