Функция os.walk()

Функция walk() модуля os принимает один обязательный аргумент - имя каталога.

Возвращает объект-генератор, из которого получают кортежи для каждого каталога переданной файловой иерархии.

Каждый кортеж состоит из трех элементов:

  1. Адрес очередного каталога в виде строки.
  2. В форме списка имена подкаталогов первого уровня вложенности для данного каталога.
  3. В виде списка имена файлов данного каталога.

Допустим, есть такое дерево каталогов:

Дерево каталогов

Передадим каталог test функции os.walk():

>>> import os
>>> tree = os.walk('test')
>>> tree
<generator object walk at 0x7f1119ca0518>
>>> for i in tree:
...     print(i)
... 
('test', ['cgi-bin'], ['dgs.png', 'index.html'])
('test/cgi-bin', ['backup', 'another'], ['hello.py'])
('test/cgi-bin/backup', [], [])
('test/cgi-bin/another', [], ['data.txt'])

Если передать абсолютный адрес, то получится так:

>>> for i in os.walk('/home/pl/test'):
...     print(i)
... 
('/home/pl/test', ['cgi-bin'], ['dgs.png', 'index.html'])
('/home/pl/test/cgi-bin', ['backup', 'another'], ['hello.py'])
('/home/pl/test/cgi-bin/backup', [], [])
('/home/pl/test/cgi-bin/another', [], ['data.txt'])

Каждый кортеж включает три элемента. Первый – адрес каталога, второй – список его поддиректорий первого уровня, третий – список имен файлов. Если вложенных каталогов или файлов нет, соответствующий список пуст.

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

>>> folder = []
>>> for i in os.walk('test'):
...     folder.append(i)
... 
>>> folder
[('test', ['cgi-bin'], ['dgs.png', 'index.html']), ('test/cgi-bin', ['backup', 'another'], ['hello.py']), ('test/cgi-bin/backup', [], []), ('test/cgi-bin/another', [], ['data.txt'])]

Теперь если потребуются полные адреса файлов можно извлекать из списка каждый кортеж, распаковывать его и соединять адрес с именем файла:

>>> for address, dirs, files in folder:
...     for file in files:
...             print(address+'/'+file)
... 
test/dgs.png
test/index.html
test/cgi-bin/hello.py
test/cgi-bin/another/data.txt

Переменная address на каждой итерации связывается с первым элементом очередного кортежа (строкой, содержащей адрес каталога), dirs – со вторым элементом (списком подкаталогов), а files - со списками файлов этого каталога. Во вложенном цикле извлекается каждый отдельный файл из списка файлов.

Можно не распаковывать кортеж, а обращаться к его элементам по индексам (0 - адрес, 1 - список каталогов, 2 - список файлов):

>>> for i in folder:
...     for j in i[2]:
...             print(i[0]+'/'+j)
... 
test/dgs.png
test/index.html
test/cgi-bin/hello.py
test/cgi-bin/another/data.txt