递归地将项添加到列表中

时间:2012-11-03 04:15:28

标签: python list recursion

您好。我想知道是否有办法递归地将项目添加到列表中。该函数应该打印与fname匹配的文件的路径名。所以fname是文件的名称,路径是文件所在的文件夹。如果路径文件夹中有文件夹,它将进入内部并查找fname文件。到目前为止,我能够找到所有文件。但是我无法递归追加列表。

def findAll(fname, path): 
 lst= []
 for item in os.listdir(path):
        n = os.path.join(path, item)
    try:
        if item == fname:
            lst.append(n)
    except:
        findAll(fname,n)
return lst

5 个答案:

答案 0 :(得分:4)

通常情况下,我不会提供完整的解决方案,因为这有点像家庭作业(这也是我为什么要避免os.walk),但既然你已经发布了你的尝试,这里有一个解释和解决方案:< / p>

首先,每次拨打findAll时,都会初始化lst。当然,你最后会返回它,但是你没有对返回值做任何事情,因此效果lst.append包含在递归中,因此在外部不可见。让我试着用图解释一下(用一级递归):

+--------------------------------------------------+
|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 from recursive call,           |
|but not assigned to a variable.                   |
|Therefore, `lst` remains unchanged                |
+--------------------------------------------------+

有几种方法可以解决这个问题:

  1. lst移至findAll以外的范围(个人而言,这就是我要做的)
  2. 使用递归调用的返回值来修改lst
  3. lst移至findAll

    之外的范围
    lst= []
    def findAll(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 here
                if item == fname:
                    lst.append(n)
                else:
                    findAll(fname,n)
            except:
                pass
    

    findAll终止后,lst将包含您想要的值

    使用递归调用的返回值来修改lst

    def findAll(fname, path, answer=None):
        if answer == None:
            answer = []
        for item in os.listdir(path):
            n = os.path.join(path, item)
            try:
                if item == fname:
                    answer += [n]
            except:
                findAll(fname,n, answer)
        return answer
    

    希望这有帮助

    PS:当然,执行此操作的非作业方式是使用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
    

    编辑:OP要求提供不使用全局变量的版本:

    def findAll(fname, root, answer=None):
        if answer == None:
            answer = []
         for entry in os.listdir(root):
             if os.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
    

答案 1 :(得分:1)

您需要使用递归调用扩展列表

list.extend(findAll(fname,n))

您也可以检查某些内容是否为os.path.isdir(n)

目录

但我认为你的问题比你的脚本更多

afaik listdir只返回名称,而不是目录的路径....

因此您需要致电findAll(fname,os.path.join(path,n))

答案 2 :(得分:0)

本身问题无关,但我相信os.walk可以帮助您:

allFiles = []
for root, dirs, files in os.walk(basedir):
    [allFiles.append(file) for file in files]

查看help(os.walk),它提供了一个关于如何使用此功能的绝佳示例。

答案 3 :(得分:0)

try/except在您的代码中使用不正确。仅在出现错误时才执行except子句。此外,您不使用findAll()的返回值。你可以跳过在函数内部创建一个列表,只是懒得产生找到的项目:

import os

def findAll(filename, rootdir): 
    for item in os.listdir(rootdir):
        path = os.path.join(rootdir, item)
        if not os.path.isdir(path):
            if item == filename: # don't select dirs
                yield path
        else: # path is a dir
            try:
                for found_path in findAll(filename, path):
                    yield found_path
            except EnvironmentError:
                pass # ignore errors

print(list(findAll('python', '/usr')))

Output

['/usr/bin/python']

如果不是家庭作业,您可以使用os.walk()查找文件:

import os

def find_all(filename, rootdir):
    for dirpath, dirs, files in os.walk(rootdir):
        for file in files:
            if file == filename:
                yield os.path.join(dirpath, file)


print(list(find_all('python', '/usr')))

Output

['/usr/bin/python']

它与预期的输出相同。

答案 4 :(得分:0)

如果您使用的是基于Unix的系统,则可以将findsubprocess模块一起使用..我认为这是检索与文件名匹配的所有路径的最快方法。然后,您可以在输出上执行split()以使其成为列表:

>>> 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

您始终可以拆分命令并避开shell=True

结帐:http://docs.python.org/2/library/subprocess.html#using-the-subprocess-module 希望这有帮助!