Add An Item Into A List Recursively
Solution 1:
Normally, I wouldn't give a full solution because this smells like homework (which is also why I'm avoiding os.walk
), but since you have posted your attempt, here's an explanation and a solution:
For one thing, every time you call findAll
, you initialize lst
. Sure, you return it at the end, but you don't do anything with the return value, so the effect lst.append
is contained within the recursion and is therefore not visible outside. Let me try to draw diagram to explain this (with one level of recursion):
+--------------------------------------------------+|Outer Level: ||||`lst = []` ||found file f1 with name fname ||`lst.append(f1)` ||+------------------------------------------------+|||Inner Level ||||||||`lst=[]` ||||found file f2 with name fname ||||`lst.append(f2)` ||||`return lst` |||+------------------------------------------------+||a list is returned fromrecursivecall, ||but not assigned to a variable. ||Therefore, `lst` remains unchanged |+--------------------------------------------------+
There are a couple of ways by which you can fix this:
- move
lst
to a scope outsidefindAll
(personally, this is what I would do) - use the return value from the recursive call to modify
lst
move lst
to a scope outside findAll
lst= []
deffindAll(fname, path):
global lst
for item in os.listdir(path):
n = os.path.join(path, item)
try: # really though, you don't need to use try/except hereif item == fname:
lst.append(n)
else:
findAll(fname,n)
except:
pass
After findAll
has terminated, lst
will contain the values you want
use the return value from the recursive call to modify lst
def findAll(fname, path, answer=None):
if answer == None:
answer = []
for item inos.listdir(path):
n = os.path.join(path, item)
try:
if item == fname:
answer += [n]
except:
findAll(fname,n, answer)
return answer
Hope this helps
PS: of course, the non-homework way to do this would be to use os.walk
:
answer = []
def findAll(fname, dirpath):
dirpath, dirnames, filenames = os.walk(dirpath)
for filename in filenames:
if filename == fname:
answer.append(os.path.join(dirpath, filename))
for dirname in dirnames:
findAll(fname, os.path.join(dirpath, dirname))
# now, answer contains all the required filepaths
EDIT: OP asked for a version that doesn't use global variables:
def findAll(fname, root, answer=None):
if answer == None:
answer = []
for entry inos.listdir(root):
ifos.path.isdir(os.path.join(root, entry)):
answer += findAll(fname, os.path.join(root, entry))
else:
if entry == fname:
answer.append(os.path.join(root, entry))
return answer
Solution 2:
you need to extend your list with your recursive call
list.extend(findAll(fname,n))
also you can check if something is a directory with os.path.isdir(n)
but I think you have more problems than that with your script
afaik listdir
just returns names , not the path of the directory ....
so you will need to call findAll(fname,os.path.join(path,n))
Solution 3:
Not related to the question per se but I believe that os.walk
would help you out:
allFiles = []
forroot, dirs, files in os.walk(basedir):
[allFiles.append(file) forfilein files]
Check out help(os.walk)
, it comes with a great example on how to use this function.
Solution 4:
try/except
is used incorrectly in your code. except
clause is executed only if there is an error. Also you don't use the returned value from findAll()
. You could skip creating a list inside the function and just yield found items lazily instead:
import os
deffindAll(filename, rootdir):
for item in os.listdir(rootdir):
path = os.path.join(rootdir, item)
ifnot os.path.isdir(path):
if item == filename: # don't select dirsyield path
else: # path is a dirtry:
for found_path in findAll(filename, path):
yield found_path
except EnvironmentError:
pass# ignore errorsprint(list(findAll('python', '/usr')))
Output
['/usr/bin/python']
if it is not homework you could use os.walk()
to find the files:
import os
def find_all(filename, rootdir):
for dirpath, dirs, files inos.walk(rootdir):
for file in files:
if file == filename:
yieldos.path.join(dirpath, file)
print(list(find_all('python', '/usr')))
Output
['/usr/bin/python']
It is the same output as expected.
Solution 5:
If you're on a Unix based system you could use find
with the subprocess
module .. I would reckon this would be the fastest way to retrieve all paths matching a filename. You can then do a split()
on the output to make it a list:
>>>import subprocess>>>lst = subprocess.check_output('find . -name "*rst"', shell=True)>>>print lst
./SphinxWorkspace/doc/chapter1.rst
./SphinxWorkspace/doc/index.rst
./SphinxWorkspace/doc/tables.rst
You can always split the command and avoid the shell=True
Checkout: http://docs.python.org/2/library/subprocess.html#using-the-subprocess-module .. Hope this helps!
Post a Comment for "Add An Item Into A List Recursively"