Скажите-ка, как вы получали списки файлов в каталоге до того, как услышали о Python? Если у вас нет опыта работы с инструментами командной строки, ответ может быть следующим: «Ну, я запускал в Windows проводник и щелкал, куда нужно». Но здесь у нас речь идет о механизмах, менее ориентированных на графический интерфейс, то есть о механизмах командной строки.
Для получения списков файлов в Unix обычно используется команда ls; в Windows списки можно создавать вводом dir в окне консоли MS—DOS.
Поскольку сценарии Python могут выполнить любую команду оболочки с помощью os.popen, они являются самым универсальным способом получения содержимого каталога из программ на языке Python. Мы уже встречались с функцией os.popen в предыдущей главе — она выполняет команду оболочки и возвращает объект файла, из которого можно прочесть вывод команды. Для иллюстрации допустим сначала, что имеется следующая структура каталогов — на моем ноутбуке с Windows есть обе команды, dir и Unix-подобная ls из Cygwin:
c:\temp> dir /B
parts
PP3E
random.bin
spam.txt temp.bin temp.txt
c:\temp> c:\cygwin\bin\ls
PP3E parts random.bin spam.txt temp.bin temp.txt
c:\temp> c:\cygwin\bin\ls parts
part0001 part0002 part0003 part0004
Имена parts и PP3E являются здесь подкаталогами, вложенным в каталог C:\temp (последний из них является копией дерева каталогов с примерами для предыдущего издания книги, часть из которых я использовал в этом издании). Теперь мы знаем, что сценарии могут получать списки имен файлов и каталогов на этом уровне, просто запуская специфическую для платформы команду и читая полученный вывод (текст, обычно выводимый в окно консоли):
C:\temp> python
> >> import os
> >> os.popen(‘dir /B’).readlines()
[‘parts\n’, ‘PP3E\n’, ‘random.bin\n’, ‘spam.txt\n’, ‘temp.bin\n’, ‘temp.txt\n’]
Строки, возвращаемые командой оболочки, содержат замыкающий символ конца строки, но его легко можно отсечь. Кроме того, функция os.popen возвращает итератор, точно такой же, как итератор объектов файлов:
> >> for line in os.popen(‘dir /B’):
… print(line[:-1])
…
parts
PP3E
random.bin
> >> lines = [line[:-1] for line in os.popen(‘dir /B’)]
> >> lines
[‘parts’, ‘PP3E’, ‘random.bin’, ‘spam.txt’, ‘temp.bin’, ‘temp.txt’]
В случае объектов каналов действие итераторов может иметь еще более значимый эффект, чем просто уход от одновременной загрузки всех результатов в память: метод readlines всегда блокирует вызывающий процесс, пока не завершится порожденная программа, тогда как при использовании итераторов этого не происходит.
Обе команды, dir и ls, позволяют задавать требуемые образцы имен файлов и имен каталогов, список содержимого которых должен быть получен, с помощью шаблонов имен. В следующем примере мы снова просто выполняем команды оболочки, поэтому годится все, что можно ввести в командной строке:
> >> os.popen(‘dir *.bin /B’).readlines()
[‘random.bin\n’, ‘temp.bin\n’]
> >> os.popen(r’c:\cygwin\bin\ls *.bin’).readlines()
[‘random.bin\n’, ‘temp.bin\n’]
> >> list(os.popen(r’dir parts /B’))
[‘part0001\n’, ‘part0002\n’, ‘part0003\n’, ‘part0004\n’]
> >> [fname for fname in os.popen(r’c:\cygwin\bin\ls parts’)]
[‘part0001\n’, ‘part0002\n’, ‘part0003\n’, ‘part0004\n’]
Эти вызовы используют универсальные инструменты и все действуют, как было заявлено. Однако выше отмечалось, что недостатками os.popen являются необходимость использования команд оболочки, специфических для платформы, и потеря производительности при запуске независимых программ. На практике различные инструменты могут возвращать различные результаты:
> >> list(os.popen(r’dir parts\part* /B’))
[‘part0001\n’, ‘part0002\n’, ‘part0003\n’, ‘part0004\n’]
>>>
> >> list(os.popen(r’c:\cygwin\bin\ls parts/part*’))
[‘parts/part0001\n’, ‘parts/part0002\n’, ‘parts/part0003\n’, ‘parts/part0004\n’]
Следующие два альтернативных приема проявляют себя лучше в обоих отношениях.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011