os.walk() другое поведение, если ссылаются на имена каталогов

#python #directory #operating-system #python-os

Вопрос:

Я изучаю Python и пишу приложение, которое рекурсирует дерево папок и идентифицирует файлы определенного расширения.

Структура тестовой папки выглядит следующим образом, в ней 10 текстовых файлов:

 C:TEMPROOT
├───dir1
│   │   dir1file1.txt
│   │   dir1file2.txt
│   │
│   ├───subdir1
│   │       dir1subdir1file1.txt
│   │       dir1subdir1file2.txt
│   │
│   └───subdir2
│           dir1subdir2file1.txt
│           dir1subdir2file2.txt
│
└───dir2
    │   dir2file1.txt
    │   dir2file2.txt
    │
    └───subdir1
        │   dir2subdir1file1.txt
        │
        └───subdir1
            └───subdir1
                    dir2subdir1subdir1subdir1file1.txt
 

Деловая часть кода, извлеченная и упрощенная, представляет собой:

 def scan_for_txt_files(start_from):
    for root_path, subdirs, files in os.walk(start_from):
        for _ in subdirs:
            # In the real application I update a progress bar here.
            for this_file in files:
                ext = str.lower(os.path.splitext(this_file)[1]).replace('.', '')
                if ext == 'txt':
                    print(f'{os.path.join(root_path, this_file)}')
 

При запуске он печатает:

 c:temprootdir1dir1file1.txt
c:temprootdir1dir1file2.txt
c:temprootdir1dir1file1.txt
c:temprootdir1dir1file2.txt
c:temprootdir2dir2file1.txt
c:temprootdir2dir2file2.txt
c:temprootdir2subdir1dir2subdir1file1.txt
 

Однако если я изменю код, чтобы удалить ссылку на субдиры, он будет работать правильно:

 def scan_for_txt_files(start_from):
    for root_path, subdirs, files in os.walk(start_from):
        for this_file in files:
            ext = str.lower(os.path.splitext(this_file)[1]).replace('.', '')
            if ext == 'txt':
                print(f'{os.path.join(root_path, this_file)}')
 

Выход:

 c:temprootdir1dir1file1.txt
c:temprootdir1dir1file2.txt
c:temprootdir1subdir1dir1subdir1file1.txt
c:temprootdir1subdir1dir1subdir1file2.txt
c:temprootdir1subdir2dir1subdir2file1.txt
c:temprootdir1subdir2dir1subdir2file2.txt
c:temprootdir2dir2file1.txt
c:temprootdir2dir2file2.txt
c:temprootdir2subdir1dir2subdir1file1.txt
c:temprootdir2subdir1subdir1subdir1dir2subdir1subdir1subdir1file1.txt
 

Первая форма кода состоит в том, что я намерен заранее определить количество вложенных папок, а затем в поле «для»… в разделе субдиры обновите индикатор выполнения на основе того, сколько папок было отсканировано.

Это происходит либо с реальной файловой системой, либо с pytestpyfakefs. Я уверен, что это что-то простое, но я не могу понять, что происходит.

Комментарии:

1. Почему вы ожидаете, что на код нужно будет ссылаться subdirs (когда ваша цель состоит только в повторении файлов и на самом деле не предполагает знания того, какие подкаталоги есть в каждом root_path из них)? Для меня не очевидно, почему ожидается , что первоначальная попытка будет правильной.

2. …когда вы перебираете поддиры, вы запускаете все внутри этого тела цикла переменное количество раз (в зависимости от того, сколько существует поддиров). Почему вы хотите такого поведения?

3. Видим ли мы вывод упрощенного сценария из оригинала? В первом из них показаны дубликаты, которых мы ожидали бы, потому что в коде for _ in subdirs: обработка файлов происходит для каждого подкаталога. Это кажется странным поступком. Но отсутствие подкаталогов наводит на мысль, что вы на самом деле меняете subdirs список, что изменит какую ОС. ходьба делает.

4. См.Отредактированный вопрос.

5. Можете ли вы сделать это запущенным сценарием, который создает дерево каталогов для тестирования>

Ответ №1:

Проблема заключается в моем неполном понимании генераторов Python, а также в неправильном отступе с моей стороны. Раздел «для этого файла в файлах» должен иметь отступ на том же уровне, что и раздел «для _ в поддирах».

Спасибо за ответы.