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

В последней команде составлена цепочка из трех сценариев на языке Python: вывод каждого предыдущего сценария соединяется с вводом последующего с помощью синтаксиса конвейера.

Альтернативные реализации сценариев adder и sorter

Если присмотреться, можно заметить, что сценарий sorter.py читает сразу все данные, имеющиеся в stdin, используя метод readlines, а сценарий adder.py читает данные по одной строке. Если источником входных данных является другая программа, то в некоторых системах соединенные каналом программы выполняются параллельно. В таких системах, особенно если пересылается большой объем данных, лучше производить построчное чтение: читающей программе не придется ждать, пока пишущая программа полностью завершит работу, чтобы заняться обработкой данных. Так как функция input просто читает данные из потока stdin, схему построчного ввода, используемую в adder.py, можно также реализовать прямым обращением к sys.stdin:

C:\...\PP4E\System\Streams> type adder2.py import sys sum = 0 while True:

line = sys.stdin.readline() if not line: break sum += int(line) print(sum)

Данная версия использует тот факт, что функция int допускает наличие пробельных символов вокруг числа (функция readline возвращает строку вместе с символом \n, но мы не должны использовать [:-1] или rstrip() для его удаления). Фактически для достижения того же эффекта можно использовать более современные итераторы файлов - цикл for, например, автоматически извлекает из объекта файла по одной строке в каждой итерации (подробнее об итераторах файлов рассказывается в следующей главе):

C:\...\PP4E\System\Streams> type adder3.py import sys

sum = 0

for line in sys.stdin: sum += int(line) print(sum)

Однако перевод сценария sorte r на построчное чтение едва ли даст большой выигрыш в производительности, потому что метод sort списков требует, чтобы весь список был заполнен. Как будет показано в главе 18, запрограммированные вручную алгоритмы сортировки, скорее всего, будут работать значительно медленнее, чем метод сортировки списка Python.

Интересно отметить, что в версии Python 2.4 и выше эти два сценария можно реализовать более компактно, используя новую встроенную функцию sorted, выражения-генераторы и итераторы файлов. Следующий сценарий действует точно так же, как и оригиналы, но имеет заметно меньший размер:

C:\...\PP4E\System\Streams> type sorterSmall.py import sys

for line in sorted(sys.stdin): print(line, end=’’)

C:\...\PP4E\System\Streams> type adderSmall.py import sys

print(sum(int(line) for line in sys.stdin))

В последнем примере функции sum в виде аргумента передается выражение-генератор, по своему поведению похожее на генератор списков, с той лишь разницей, что оно возвращает результаты по одному значению, а не в виде списка. В результате сценарий получается более компактным. За дополнительной информацией обращайтесь к ресурсам по основам языка, таким как книга «Изучаем Python».

Перенаправление потоков и взаимодействие с пользователем

Выше в этом разделе мы направили вывод сценария teststreams.py на вход стандартной программы командной строки more с помощью следующей команды:

C:\...\PP4E\System\Streams> python teststreams.py < input.txt | more

Но поскольку в предыдущей главе мы уже написали на языке Python собственную утилиту «more» постраничного вывода, почему не сделать так, чтобы она тоже принимала ввод из stdin? Например, если изменить последние три строки в файле more.py, представленном в примере 2.1, на следующие:

if __name__ == ‘__main__’:    # если выполняется, а не импортируется

import sys

if len(sys.argv) == 1:    # вывести данные из stdin, если нет аргументов

more(sys.stdin.read())

else:

more(open(sys.argv[1]).read())

Тогда, похоже, мы сможем перенаправить стандартный вывод сценария teststreams.py на стандартный ввод more.py:

C:\...\PP4E\System\Streams> python teststreams.py < input.txt | python ..\more. py

Hello stream world Enter a number>8 squared is 64 Enter a number>6 squared is 36 Enter a number>Bye

В целом такой прием можно использовать в сценариях на языке Python. Здесь сценарий teststreams.py снова принимает данные из файла. И, как и в предыдущем разделе, вывод одной программы отправляется по каналу на ввод другой - сценарий more.py в родительском (. ) каталоге.

Однако в предыдущей команде more^Y кроется малозаметная проблема. В действительности эта цепочка сработала по чистой случайности: если первый сценарий будет выводить слишком много данных и сценарию more придется спрашивать пользователя о разрешении продолжить вывод, произойдет полный отказ сценария (точнее, когда функция input возбудит исключение EOFError).

Проблема в том, что улучшенный вариант more^ использует stdin для достижения двух различных целей. Он читает ответ пользователя из stdin, вызывая input, но теперь еще и принимает из stdin основные входные данные. Когда поток stdin подключается к входному файлу или каналу, его нельзя использовать для получения ответа от пользователя, потому что он содержит входные данные. Кроме того, поскольку перенаправление потока stdin происходит еще до запуска программы, невозможно узнать, был ли он перенаправлен в командной строке.