05 марта 2009

Обход дерева каталогов в Python

Очередной раз поразился прозрачности синтаксиса и мощности стандартной библиотеки Python. Передо мной стояла простая, но неприятная задача: прочитать из файла список каталогов, обойти их, найдя все mp3-файлы и сформировать из них плей-лист на 10 песен в случайном порядке.
Считывание файла со списком каталогов вполне традиционно:

with open("c:\\dirs.conf", "r") as f:
directories = f.read()

Т.к. названия каталогов разбиты переводом строки, то, тоже вполне традиционно, я преобразовал считанную из файла строку в список функцией split. И приготовился писать рекурсивную процедуру обхода дерева каталогов… Но меня остановила интуиция, и я отправился поискать готовых решений в google. Да, в Python оказалась функция os.walk(), которая, начиная с указанного пути обходит все вложенные директории. Правда результат возвращает в довольно странном виде, что-то вроде списка кортежей списков. И опять, я начал было писать разбор всего этого, но google подсказал изящное решение в стиле функционального pattern-matching. В результате, обход дерева вместе с обходом списка каталогов из файла занял 2 строки:
for directory in directories.split("\n"):
for root, dirs, files in os.walk(directory):

Для получения списка mp3 нашлось не менее изящное решение на основе list comprehensive (os.path.join() платформо-безопасно лепит путь из имен каталога и файла):
MP3files += [os.path.join(root, name) for name in files if name[-3:] == "mp3"]
Здесь меня особенно порадовали срезы [−3:] — 3 символа с конца. На C++ или Java это было бы что-то тяжеловесное: substr(length()-3, length()) и т.п.
Наконец, получение списка случайных файлов одной строкой с использованием функции random.choice() — выбрать случайный элемент из списка:
return [random.choice(MP3files) for i in xrange(10)]
Всего 5 строк!

P.S. Не могу найти приличного Syntax Highlighter'а для Python. В этот раз использовал http://www.tohtml.com/, он with не подсветил:( Может самому написать?

3 комментария:

Imbolc комментирует...

Подсветка: http://softwaremaniacs.org/soft/highlight/

Imbolc комментирует...

Первые три строчки тоже лишние :)
Достаточно:

for root, dirs, files in os.walk("c:\\dirs.conf"):

Native-born citizen комментирует...

За подсветку спасибо, посмотрю, а что касается 3 лишних строк, то dirs.conf - это файл, который содержит список директорий, которые надо просмотреть. Он не может быть параметром функции walk()