Вы дважды сканируя каждый путь, когда-то неявно с помощью walk
, затем снова явно повторно scandir
тами path
walk
вернулся без причины. walk
уже вернул files
, так что внутренний контур может избежать двойного сканирования, используя только то, что оно было дано:
def subdirs(path):
for path, folders, files in walk(path):
for file in files:
if '.xlsm' in file:
yield os.path.join(path, file)
Для решения обновленного вопроса, вы, вероятно, хотите или скопировать существующий scandir.walk
код и изменить он должен вернуть list
s из DirEntry
s вместо list
с именами или написать аналогичный специальный код с обложкой для ваших нужд; в любом случае это позволит избежать двойного сканирования, сохраняя при этом низкое накладное поведение scandir
. Например:
def scanwalk(path, followlinks=False):
'''Simplified scandir.walk; yields lists of DirEntries instead of lists of str'''
dirs, nondirs = [], []
for entry in scandir.scandir(path):
if entry.is_dir(follow_symlinks=followlinks):
dirs.append(entry)
else:
nondirs.append(entry)
yield path, dirs, nondirs
for dir in dirs:
for res in scanwalk(dir.path, followlinks=followlinks):
yield res
Вы можете заменить использование walk
с ним, как это (я также добавил код, чернослив каталоги с Test
в них, так как все каталоги и файлы под ними были бы отвергнуты исходным кодом, но вы все еще пройти их без надобности):
def subdirs(path):
# Full prune if the path already contains Test
if 'Test' in path:
return
for path, folders, files in scanwalk(path):
# Remove any directory with Test to prevent traversal
folders[:] = [d for d in folders if 'Test' not in d.name]
for file in files:
if '.xlsm' in file.path:
yield file.stat() # Maybe just yield file to get raw DirEntry?
for i in subdirs('O:\\'):
print i
Кстати, вы можете перепроверить, что вы правильно установили/построил C ускоритель для scandir
, _scandir
. Если _scandir
не построен, модуль scandir
обеспечивает резервные реализации с использованием ctypes
, но они значительно медленнее, что может объяснить проблемы с производительностью. Попробуйте запустить import _scandir
в интерактивной сессии Python; если он поднимает ImportError
, то у вас нет ускорителя, поэтому вы используете медленную резервную реализацию.
Единственная проблема заключается в том, что я теряю другие переменные, связанные с файлами, которые мне обычно нужно получить через 'os.stat()' см: https://github.com/benhoyt/scandir – user2242044
@ user2242044: Я знаю про «скандир» (я регулярно евангелизую его), но ваш исходный код был только «yield» по пути, а не к объекту «DirEntry»; вы не использовали бесплатное/кэшированное поведение 'stat'. Если вам нужна информация 'stat', вы можете скопировать реализацию' scandir.walk' и изменить ее, чтобы возвращать 'list' из исходных объектов 'DirEntry' для' dirnames' и 'filenames' вместо возврата' list' из 'str'. Это становится лучшим из обоих миров; однопроходное сканирование, не повторяющееся 'stat'ing. Или просто реализуйте свою собственную рекурсивную функцию, которая специализируется на вашем случае использования. – ShadowRanger
@ ShadowRanger .. Спасибо за разъяснение. Я попытался упростить код для вопроса SO, но думаю, что я потерял некоторые намерения. Двойная итерация состояла в том, что я старался избегать повтора нескольких папок, и я хочу получить доход от статистики. Изменен исходный вопрос. Благодаря! – user2242044