Перенаправление потоков в объекты Python

perenapravlenie potokov v obekty python Контекст выполнения сценариев

Все приведенные выше способы перенаправления стандартных потоков действуют для программ, написанных на любом языке программирования, который обеспечивает возможность перехватывать стандартные потоки, и зависят скорее от процессора командной строки оболочки, чем от самого интерпретатора. Операции перенаправления в командной строке, такие как < filename и | program, обрабатываются оболочкой, а не интерпретатором Python. Более «питонистый» способ перенаправления можно реализовать в самих сценариях, присваивая переменным sys.stdin и sys.stdout объекты, похожие на файлы.

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

     Любой объект, обладающий методами чтения, может быть присвоен переменной sys.stdin, в результате чего ввод будет осуществляться через методы чтения этого объекта.

     Любой объект, обладающий методами записи, может быть присвоен переменной sys.stdout; в результате весь стандартный вывод будет отправляться методам этого объекта.

Так как функции print и input просто вызывают методы write и readline объектов, на которые ссылаются sys.stdout и sys.stdin, мы можем генерировать и перехватывать стандартные текстовые потоки с помощью объектов, реализованных с помощью классов.

Если вам уже приходилось изучать язык Python, вы, вероятно, знаете, что подобная совместимость по интерфейсам обычно называется полиморфизмом — неважно, чем является объект, и неважно, что делают его методы, — важно, чтобы этот объект предоставлял ожидаемый интерфейс. Такое либеральное отношение к типам данных объясняет значительную долю гибкости и выразительности программного кода на языке Python. Ниже демонстрируется способ, позволяющий сценариям переопределять собственные потоки ввода-вывода. В примере 3.9 приводится вспомогательный модуль, демонстрирующий эту идею.

Пример 3.9. PP4E\System\Streams\redirect.py

объекты, похожие на файлы, один из которых сохраняет в строке текст, отправленный в стандартный поток вывода, а другой обеспечивает ввод текста из строки в стандартный поток ввода; функция redirect вызывает переданную ей функцию, для которой стандартные потоки вывода и ввода будут связаны с объектами, похожими на файлы;

подпись: import sys# импортировать встроенный модуль

class Output:

def __init__(self): self.text = ‘’

def write(self, string):

self.text += string

подпись: # перенаправляет stdin/out
# вызывает объект функции
# возвращает текст в stdout
# вызвать функцию с аргументами
# восстановить, независимо от
# того, было ли исключение
# вернуть результат,
# если исключения не было
def writelines(self, lines):

подпись: # имитирует выходной файл
# при создании строка пустая
# добавляет строку байтов
# добавляет все строки в список


for line in lines: self.write(line)


 


 

def redirect(function, pargs, kargs, input) savestreams = sys.stdin, sys.stdout sys.stdin = Input(input) sys.stdout = Output() try:

result = function(*pargs, **kargs) output = sys.stdout.text finally:

sys.stdin, sys.stdout = savestreams return (result, output)

В этом модуле определены два класса, маскирующиеся под настоящие файлы:

Output

Предоставляет интерфейс (он же протокол) метода записи, предполагаемый у выходных файлов, но сохраняет всю записываемую информацию в строке, хранящейся в памяти.

Input

Предоставляет интерфейс, предполагаемый у входных файлов, но возвращает входные данные по требованию, извлекая их из хранящейся в памяти строки, переданной при создании объекта.

Функция redirect в конце этого файла объединяет эти два объекта, чтобы выполнить единственную функцию, для которой стандартные потоки ввода и вывода будут перенаправлены в объекты Python. Функции, которая вызывается функцией redirect, не требуется ни знать, ни заботиться о том, что вызываемые ею функции print и input или методы stdin и stdout в действительности будут иметь дело с нашими объектами, а не с настоящим файлом, каналом или пользователем.

Чтобы продемонстрировать, как действует эта функция, импортируем и вызовем функцию interact, лежащую в основе сценария teststreams, представленного в примере 3.5, который прежде мы запускали из командной строки (для использования вспомогательной функции перенаправления нужно действовать на языке функций, а не файлов). При непосредственном вызове функция читает данные с клавиатуры и выводит результаты на экран, как если бы она выполнялась как программа без перенаправления:

C:\\PP4E\System\Streams> python

>>> from teststreams import interact

>>> interact()

Hello stream world

Enter a number>2

2  squared is 4 Enter a number>3

3  squared is 9 Enter a number"Z Bye

>>> 

Теперь вызовем эту функцию под управлением функции перенаправления в redirect.py и передадим ей некоторый готовый входной текст. В этом случае на вход функции interact поступит переданная строка (‘4\n5\n6\n’ — три строки с явными символами конца строки), а результатом выполнения функции будет кортеж, содержащий возвращаемое значение и строку с текстом, который был записан в стандартный поток вывода:

>>> from redirect import redirect

>>> (result, output) = redirect(interact, (), {}, ‘4\n5\n6\n’)

>>> print(result) None

>>> output

‘Hello stream world\nEnter a number>4 squared is 16\nEnter a number>5 squared is 25\nEnter a number>6 squared is 36\nEnter a number>Bye\n’

На выходе получится одна длинная строка, содержащая весь текст, записанный в стандартный поток вывода. Чтобы улучшить внешний вид строки, ее можно передать функции print или разбить на отдельные строки с помощью строкового метода splitlines:

>  >> for line in output.splitlines(): print(line)

Hello stream world

Enter a number>4 squared is 16

Enter a number>5 squared is 25

Enter a number>6 squared is 36 Enter a number>Bye

Еще лучше повторно использовать модуль more.py, который мы написали в предыдущей главе (пример 2.1). При этом придется меньше запоминать и вводить с клавиатуры, а качество работы уже проверено нами (ниже, как и во всех примерах, где выполняется импортирование модулей из других каталогов, предполагается, что каталог, содержащий корневой подкаталог PP4E, находится в пути поиска модулей, — измените значение переменной окружения PYTHONPATH, если это необходимо):

>  >> from PP4E.System.more import more

>  >> more(output) Hello stream world Enter a number>4 squared is 16 Enter a number>5 squared is 25 Enter a number>6 squared is 36 Enter a number>Bye

Конечно, это искусственный пример, но продемонстрированные приемы могут иметь широкое применение. Например, в программу, реализующую интерфейс командной строки для взаимодействия с пользователем, легко добавить графический интерфейс. Нужно просто перехватить стандартный вывод с помощью объекта, такого как экземпляр класса Output, и сбросить текстовую строку в окно. Аналогично стандартный ввод можно перенаправить в объект, который получает текст из графического интерфейса (например, из окна диалога). Поскольку классы могут заменять настоящие файлы, их можно использовать в любых инструментах, работающих с файлами. Обратитесь к модулю перенаправления потоков с графическим интерфейсом guiStreams в главе 10, где вы найдете конкретную реализацию некоторых из описанных идей.

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Оцените статью
Секреты программирования
Добавить комментарий