Подтвердить что ты не робот

Выход в рекурсивной функции

Я пытаюсь сделать что-то во всех файлах по заданному пути. Я не хочу заранее собирать все имена файлов, а затем что-то делать с ними, поэтому я попробовал это:

import os
import stat

def explore(p):
  s = ''
  list = os.listdir(p)
  for a in list:
    path = p + '/' + a
    stat_info = os.lstat(path )
    if stat.S_ISDIR(stat_info.st_mode):
     explore(path)
    else:
      yield path

if __name__ == "__main__":
  for x in explore('.'):
    print '-->', x

Но этот код пропускает каталоги, когда он ударяет их, вместо того, чтобы уступать их содержимому. Что я делаю неправильно?

4b9b3361

Ответ 1

Используйте os.walk вместо того, чтобы изобретать колесо.

В частности, следуя примерам в библиотечной документации, вот непроверенная попытка:

import os
from os.path import join

def hellothere(somepath):
    for root, dirs, files in os.walk(somepath):
        for curfile in files:
            yield join(root, curfile)


# call and get full list of results:
allfiles = [ x for x in hellothere("...") ]

# iterate over results lazily:
for x in hellothere("..."):
    print x

Ответ 2

Итераторы не работают так рекурсивно. Вы должны повторно получить каждый результат, заменив

explore(path)

с чем-то вроде

for value in explore(path):
    yield value

Python 3.3 добавил синтаксис yield from X, как предлагается в PEP 380, чтобы служить для этой цели, С его помощью вы можете сделать это:

yield from explore(path)

Если вы используете генераторы как сопрограммы, этот синтаксис также поддерживает использование generator.send() для передачи значений обратно в рекурсивно-вызванные генераторы. Простой цикл for выше не будет.

Ответ 3

Проблема заключается в этой строке кода:

explore(path)

Что он делает?

  • вызывает explore с новым path
  • explore выполняется, создавая генератор
  • генератор возвращается в то место, где explore(path) был выполнен .,.
  • и отбрасывается

Почему он отбрасывается? Он не был назначен ни на что, он не повторялся - он полностью игнорировался.

Если вы хотите что-то сделать с результатами, вам нужно что-то сделать с ними!;)

Самый простой способ исправить ваш код:

for name in explore(path):
    yield name

Когда вы уверены, что понимаете, что происходит, вы, скорее всего, захотите использовать os.walk().

Как только вы перейдете на Python 3.3 (при условии, что все будет выполнено по плану), вы сможете использовать новый синтаксис yield from, и самый простой способ исправить ваш код в этой точке будет:

yield from explore(path)

Ответ 4

Измените это:

explore(path)

Для этого:

for subpath in explore(path):
    yield subpath

Или используйте os.walk, как предположил phooji (что является лучшим вариантом).

Ответ 5

Это вызывает explore как функцию. То, что вы должны сделать, это перебрать его как генератор:

if stat.S_ISDIR(stat_info.st_mode):
  for p in explore(path):
    yield p
else:
  yield path

EDIT: вместо модуля stat вы можете использовать os.path.isdir(path).

Ответ 6

Попробуйте следующее:

if stat.S_ISDIR(stat_info.st_mode):
    for p in explore(path):
        yield p

Ответ 7

os.walk отлично, если вам нужно пройти все папки и подпапки. Если вам это не нужно, ему нравится использовать пистолет-слон, чтобы убить муху.

Однако для этого конкретного случая os.walk может быть лучшим подходом.

Ответ 8

Вы также можете реализовать рекурсию, используя стек.

На самом деле нет никакого преимущества в этом, кроме того, что это возможно. Если вы используете python в первую очередь, увеличение производительности, вероятно, не стоит.

import os
import stat

def explore(p):
    '''
    perform a depth first search and yield the path elements in dfs order
        -implement the recursion using a stack because a python can't yield within a nested function call
    '''
    list_t=type(list())
    st=[[p,0]]
    while len(st)>0:
        x=st[-1][0]
        print x
        i=st[-1][1]

        if type(x)==list_t:
            if i>=len(x):
                st.pop(-1)
            else:
                st[-1][1]+=1
                st.append([x[i],0])
        else:
            st.pop(-1)
            stat_info = os.lstat(x)
            if stat.S_ISDIR(stat_info.st_mode):
                st.append([['%s/%s'%(x,a) for a in os.listdir(x)],0])
            else:
                yield x

print list(explore('.'))