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?
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
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.
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 nowympath
-
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)
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ą).
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)
.
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
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.
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('.'))
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