os.walk()
не использует os.listdir()
.Он использует гораздо более быструю os.scandir()
функцию , которая предоставляет итератору больше информации для каждой записи каталога:
Использование scandir()
вместо listdir()
может значительно повысить производительностькода, который также нуждается в информации о типе файла или атрибуте файла, поскольку объекты os.DirEntry
предоставляют эту информацию, если операционная система предоставляет ее при сканировании каталога.Все методы os.DirEntry
могут выполнять системный вызов, но is_dir()
и is_file()
обычно требуют системного вызова только для символических ссылок;os.DirEntry.stat()
всегда требует системного вызова в Unix, но требует только одного для символических ссылок в Windows.
Код os.walk()
интенсивно использует вызов DirEntry.is_dir()
, который с os.scandir()
равеннамного дешевле, чем использовать os.isdir()
(который должен совершать отдельные os.stat()
вызовы).
Затем ваш код слишком часто вызывает os.isdir()
.Вы фактически вызываете его дважды для каждой записи в вашем пути.Вы уже собрали все подкаталоги в y
, вам не нужно снова проверять пути при воссоздании var
.Эти дополнительные isdir()
звонки стоят вам много времени.
Вы также рекурсируете, когда var
равен пусто (без дополнительных подкаталогов), в результате чего вы сначала переносите пустой список в другой список, после чего os.listdir()
создает исключение TypeError
который ваше одеяло Pokemon-catch-em-all, кроме обработчика, заставляет замолчать.
Далее вы должны избавиться от глобальных переменных и использовать правильные имена переменных.files
и dirs
будут гораздо более понятными именами, чем y
и z
.Поскольку вы сделали глобалы y
и z
, вы сохраняете все имена файлов и каталогов для данного уровня, и для каждого первого включенного подкаталога вы затем повторно сообщаете те же самые имена файлов и каталогов, как будто они являются членами этихподкаталоги.Только после достижения первого листа такого дерева каталогов (без дополнительных подкаталогов) выполняются вызовы .clear()
для y
и z
, что приводит к очень запутанным результатам с повторяющимися именами файлов.
Вы можете изучить исходный код os.walk()
, но если мы упростим его до использования только нисходящего обхода и не будем обрабатывать ошибки, то получится:
def walk(top):
dirs = []
nondirs = []
with os.scandir(top) as scandir_it:
for entry in scandir_it:
if entry.is_dir():
dirs.append(entry.name)
else:
nondirs.append(entry.name)
yield top, dirs, nondirs
for dirname in dirs:
new_path = os.path.join(top, dirname)
yield from walk(new_path)
Примечаниечто глобальные переменные не используются ;в этом алгоритме просто нет необходимости.В каждом каталоге существует только один вызов os.scandir()
, а переменная dirs
повторно используется для рекурсии в подкаталоги.