Выбрать главу

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^Y, который мы написали в предыдущей главе (пример 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, где вы найдете конкретную реализацию некоторых из описанных идей.

Вспомогательные классы io.StringIO и io.BytesIO

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

>>> from io import StringIO

>>> buff = StringIO()    # сохраняет записываемый текст в строке

>>> buff.write('spam\n')

5

>>> buff.write('eggs\n')

5

>>> buff.getvalue()

‘spam\neggs\n’

>>> buff = StringIO('ham\nspam\n') # возвращает входные данные из строки >>> buff.readline()

‘ham\n’

>>> buff.readline()

‘spam\n’

>>> buff.readline()

Экземпляры класса StringIO могут присваиваться переменным sys.stdin и sys.stdout, как демонстрировалось в предыдущем разделе, с целью перенаправить потоки для функций input и print, и использоваться в любом программном коде, выполняющем операции с настоящими объектами файлов. Напомню еще раз, что в языке Python правила игры определяются интерфейсом объекта, а не его конкретным типом:

>>> from io import StringIO

>>> import sys

>>> buff = StringIO()

>>> temp = sys.stdout >>> sys.stdout = buff

>>> print(42, 'spam', 3.141)    # или print(..., file=buff)

>>> sys.stdout = temp    # восстановит оригинальный поток

>>> buff.getvalue()

‘42 spam 3.141\n’

Следует также отметить, что существует класс io. BytesIO, обладающий похожим поведением, но он отображает операции с файлами не на строку типа str, а на буфер байтов типа bytes:

>>> from io import BytesIO >>> stream = BytesIO()

>>> stream.write(b'spam')

>>> stream.getvalue()

b’spam’

>>> stream = BytesIO(b'dpam')

>>> stream.read()

b’dpam’

Из-за существенных различий между текстовыми и двоичными данными в Python 3.X эта альтернатива может оказаться более подходящей для сценариев, имеющих дело с двоичными данными. Подробнее с проблемой различий между текстовыми и двоичными данными мы познакомимся в следующей главе, когда займемся исследованием файлов.

Перехват потока stderr

Мы сосредоточились на перенаправлении stdin и stdout, но поток stderr также можно перенаправлять в файлы, каналы и объекты. Несмотря на то, что некоторые оболочки поддерживают возможность перенаправления этого потока, тем не менее это также можно сделаеть легко и просто в сценарии Python. Например, присвоение переменной sys. stderr экземпляра класса, такого как Output или StringIO из предыдущего примера, позволит сценарию перехватывать также текст, записываемый в стандартный поток ошибок.

Сам интерпретатор Python использует стандартный поток ошибок для вывода сообщений об ошибках (графический интерфейс IDLE перехватывает этот текст и по умолчанию окрашивает его в красный цвет). Однако в языке отсутствуют высокоуровневые инструменты для работы со стандартным потоком ошибок, такие как функции print и input для стандартных потоков вывода и ввода. Если вам потребуется организовать вывод в стандартный поток ошибок, вы можете явно вызвать метод sys.stderr.write() или прочитать следующий раздел, где описывается одна особенность функции print, упрощающая эту возможность.