Wydajność w funkcji rekurencyjnej

Próbuję zrobić coś ze wszystkimi plikami pod daną ścieżką. Nie chcę wcześniej zbierać wszystkich nazw plików, a potem coś z nimi zrobić, więc próbowałem tego:

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

Ale ten kod przeskakuje nad katalogami, gdy je uderza, zamiast oddawać ich zawartość. Co robię źle?

Author: Jeremy Banks, 2011-07-20

8 answers

Użycie os.walk zamiast wymyślać koło na nowo.

W szczególności, podążając za przykładami w dokumentacji biblioteki, oto próba nieprzetestowana:

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
 26
Author: phooji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-20 01:08:47

Iteratory nie działają rekurencyjnie w ten sposób. Musisz ponownie uzyskać każdy wynik, zastępując

explore(path)

Z czymś w rodzaju

for value in explore(path):
    yield value

Python 3.3 dodał składnię yield from X, jak zaproponowano w PEP 380 , aby służyć temu celowi. Z nim możesz zrobić to zamiast:

yield from explore(path)

Jeśli używasz generatorów jako koroutines , ta składnia obsługuje również użycie generator.send() przekazywanie wartości z powrotem do wywoływanych rekurencyjnie generatorów. The simple for pętla powyżej nie będzie.

 124
Author: Jeremy Banks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-18 14:17:44

Problemem jest ta linijka kodu:

explore(path)

Co to robi?

  • połączenia explore z nowym path
  • explore działa, tworząc generator
  • generator jest zwracany do miejsca, w którym explore(path) został wykonany . . .
  • i jest odrzucany

Dlaczego jest odrzucony? Nie był do niczego przypisany , nie był iterowany, został całkowicie zignorowany.

Jeśli chcesz coś zrobić z wynikami, cóż, masz żeby coś z nimi zrobić! ;)

Najprostszym sposobem na poprawienie kodu jest:

for name in explore(path):
    yield name

Kiedy jesteś pewien, że rozumiesz, co się dzieje, prawdopodobnie będziesz chciał użyć os.walk() zamiast tego.

Po migracji do Pythona 3.3 (zakładając, że wszystko działa zgodnie z planem) będziesz mógł użyć nowej składni yield from i najprostszym sposobem na poprawienie kodu w tym momencie będzie:

yield from explore(path)
 35
Author: Ethan Furman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-16 01:20:16

Zmień to:

explore(path)

Do tego:

for subpath in explore(path):
    yield subpath

Lub użyj os.walk, Jak zasugerował phooji (co jest lepszą opcją).

 8
Author: Dietrich Epp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-20 01:07:03

Wywołujący {[1] } Jak funkcję. To, co powinieneś zrobić, to iterację go jak generatora:

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

EDIT: zamiast modułu stat możesz użyć os.path.isdir(path).

 3
Author: MRAB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-20 01:06:02

Spróbuj tego:

if stat.S_ISDIR(stat_info.st_mode):
    for p in explore(path):
        yield p
 2
Author: satoru,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-20 01:05:30

Os.spacer jest świetny, jeśli chcesz przejść wszystkie foldery i podfoldery. Jeśli tego nie potrzebujesz, to jak użycie pistoletu na słonie do zabicia muchy.

Jednak w tym konkretnym przypadku, os.spacer może być lepszym podejściem.

 0
Author: Robson França,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-01 03:35:19

Można również zaimplementować rekurencję za pomocą stosu.

Nie ma w tym żadnej korzyści poza faktem, że jest to możliwe. Jeśli używasz Pythona w pierwszej kolejności, zyski z wydajności prawdopodobnie nie są warte zachodu.
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('.'))
 0
Author: user1149913,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-12 20:41:29