В главе 4 мы будем исследовать итераторы файлов, но перед тем, как взять эту книгу, вы наверняка уже ознакомились с основами. Поскольку объекты, возвращаемые функцией os.popen, обладают итераторами, позволяющими читать данные по одной строке за раз, использование метода readlines этих объектов обычно является излишним. Например, ниже приводится пример чтения строк, которые выводятся другой программой, без явного использования методов чтения:
> >> import os
> >> for line in os.popen(‘dir /B *.py’): print(line, end=’’) helloshell.py more.py __init__.py
Интересно, что в Python 3.1 функция os.popen реализована с использованием объекта subprocess.Popen, с которым мы познакомились в этой главе. Вы можете убедиться в этом, заглянув в файл os.py в стандартной библиотеке Python (в Windows вы найдете этот файл в каталоге C:\Python31\Lib). Результатом вызова функции os.popen является объект, управляющий объектом Popen и его каналами:
> >> I = os.popen(‘dir /B *.py’)
>>> I
<os._wrap_close object at 0x013BC750>
Этот объект-обертка канала определяет метод __iter__, поэтому он поддерживает возможность итераций по строкам, которые могут выполняться автоматически (например, в цикле for, как показано выше) или вручную. Любопытно отметить, что, несмотря на наличие в объекте-обертке поддержки прямого вызова метода __next__, как если бы этот объект обладал собственным итератором (подобно простым файлам), тем не менее он не поддерживает встроенную функцию next, хотя последняя, вероятно, просто вызывает метод __next__:
> >> I = os.popen(‘dir /B *.py’)
> >> I.__next__() ‘helloshell.py\n’
> >> I = os.popen(‘dir /B *.py’)
> >> next(I)
TypeError: _wrap_close object is not an iterator
Причина такого поведения скрыта глубоко в недрах реализации — прямой вызов метода __next__ перехватывается методом __ge— tattr__, определенном в объекте-обертке канала, и преобразуется в вызов метода обернутого объекта. Но функция next обращается к механизму перегрузки операторов, который в Python 3.X действует в обход метода __getattr__, когда производится вызов методов со специальными именами, такими как __next__. Поскольку объект-обертка канала не определяет собственный метод __next__, обращение к нему не перехватывается и не передается обернутому объекту, что приводит к ошибке при вызове встроенной функции next. Как детально объясняется в книге «Изучаем Python», метод __getattr__ обертки не вызывается по той простой причине, что в Python 3.X поиск методов начинается не с экземпляра, а с класса.
Такое поведение может быть или не быть ожидаемым, но вам не придется беспокоиться об этом при выполнении итераций по строкам в канале с помощью цикла for, генераторов и других инструментов. Тем не менее, чтобы обеспечить выполнение итераций вручную, необходимо сначала вызвать встроенную функцию iter — она вызовет метод __iter__ объекта-обертки канала и обеспечит корректную поддержку обоих способов перемещения по строкам:
> >> I = os.popen(‘dir /B *.py’)
> >> I = iter(I) # так поступает цикл for
> >> I.__next__() # теперь обе формы итераций действуют правильно
‘helloshell.py\n’
> >> next(I)
‘more.py\n’