【问题标题】:Add an item into a list recursively以递归方式将项目添加到列表中
【发布时间】:2023-04-06 12:28:01
【问题描述】:

嗨。我想知道是否有一种方法可以递归地将项目添加到列表中。该函数应该打印与 fname 匹配的文件的路径名。所以 fname 是文件名,path 是文件所在的文件夹。如果路径文件夹中有文件夹,它将进入并查找 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

【问题讨论】:

  • 我会看看os.walk

标签: python list recursion


【解决方案1】:

通常,我不会给出完整的解决方案,因为这闻起来像家庭作业(这也是我避免使用os.walk 的原因),但既然你已经发布了你的尝试,这里有一个解释和一个解决方案:

一方面,每次调用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

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

【讨论】:

  • findAll() inside except 在普通情况下不会被调用(不进行递归调用)。在这种情况下应避免使用全局变量,要么将 answer 作为参数传递,要么将 use yield
  • @J.F.Sebastian:适当地注意到except。我不想增加产量,因为这可能是为了家庭作业,因此 OP 可能处于非常初级的水平。不过我同意,应该使用yield
  • 有什么办法不使用全局变量/
【解决方案2】:

如果您使用的是基于 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 .. 希望这会有所帮助!

【讨论】:

    【解决方案3】:

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

      与问题无关本身,但我相信os.walk 会帮助你:

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

      查看help(os.walk),它附带了一个很好的例子来说明如何使用这个功能。

      【讨论】:

        【解决方案5】:

        您需要通过递归调用扩展您的列表

        list.extend(findAll(fname,n))
        

        你也可以用os.path.isdir(n)检查某个目录是否是一个目录

        但我认为您的脚本存在更多问题

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

        所以您需要致电findAll(fname,os.path.join(path,n))

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-17
          • 2020-01-30
          • 1970-01-01
          • 2013-03-07
          • 1970-01-01
          • 2016-08-12
          • 2019-03-12
          • 2022-01-17
          相关资源
          最近更新 更多