【问题标题】:How to use lists in recursive functions Python?如何在递归函数Python中使用列表?
【发布时间】:2017-11-27 17:59:09
【问题描述】:

我正在编写一个递归遍历文件系统的函数,并返回带有.txt 扩展名的所有文件的列表。

pass_test_func 参数只是一个可以运行和检查的函数(即文件是否大于 100 字节等) - nothing 函数(设置为默认值)仅返回其参数。

我的实现:

def visit(dname, pass_test_func=nothing):           
    directory = os.listdir(dname)                   
    byte_list = []
    for file in directory:
        file_dir = os.path.join(dname, file)
        if os.path.isfile(file_dir) and file_dir.lower().endswith('.txt'):
            size = os.path.getsize(file_dir)
            if pass_test_func(size):
                byte_list.append(str(size) + ' ' + file_dir)
        elif os.path.isdir(file_dir):
            visit(file_dir, pass_test_func)
    return byte_list

我的问题是当我在以下几行中递归调用visit

elif os.path.isdir(file_dir):
                visit(file_dir, pass_test_func)

byte_list 再次被清空。我明白为什么会发生这种情况,但不知道我将如何解决它。该列表必须在visit 的定义中定义,所以每当我使用递归时,它总是会被重置,不管是什么权利?也许其他一些数据结构更适合,比如元组或字典?

【问题讨论】:

  • 您应该知道已经有人努力解决您的问题:docs.python.org/3/library/os.html#os.scandir
  • @KlausD.:或者在较旧的 Python 上,您可以使用 the PyPI scandir moduleos.scandir 是基于此)。在 Windows 上,这会将系统调用(和相关的 I/O)开销从每个目录三个调用(检查是否文件、检查是否目录、列出目录)+每个文件两个调用(检查是否文件、获取大小)减少到只有一个检查每个目录;在 Linux 上,它无法避免使用 stat 进行大小检查,但它仍然减少了每个目录一次调用加上每个文件一次调用(DirEntry 对象上免费提供了类型检查)。
  • @KlausD.:请注意,在大多数情况下,您最好只使用os.walk(这是基于os.scandir,一旦os.scandir 可用),因为它可以处理很多事情给你的东西。整个函数简化为 def visit(dname, pass_test_func=nothing): return [os.path.join(root, f) for root, _, files in os.walk(dname) for f in files if pass_test_func(os.path.getsize(os.path.join(root, f)))]os.walk (单行,因为这是一个注释;你可以很容易地编写一个适当的生成器函数,它不会一遍又一遍地重复join rootf )。

标签: python python-3.x list recursion


【解决方案1】:

您的函数返回byte_list,因此只需在进行递归调用时附加返回的值,而不是像现在这样将其丢弃:

elif os.path.isdir(file_dir):    
    byte_list += visit(file_dir, pass_test_func)

【讨论】:

    【解决方案2】:

    添加一个可用于递归情况的可选参数:

    # Using * makes byte_list keyword-only, so it can't be passed by normal callers by accident
    def visit(dname, pass_test_func=nothing, *, byte_list=None):           
        directory = os.listdir(dname)           
    
        # When not passed explicitly, initialize as empty list
        if byte_list is None:
            byte_list = []
        for file in directory:
            file_dir = os.path.join(dname, file)
            if os.path.isfile(file_dir) and file_dir.lower().endswith('.txt'):
                size = os.path.getsize(file_dir)
                if pass_test_func(size):
                    byte_list.append(str(size) + ' ' + file_dir)
            elif os.path.isdir(file_dir):
                # Pass explicitly to recursive call
                visit(file_dir, pass_test_func, byte_list=byte_list)
        return byte_list
    

    作为替代方案,作为suggested by Blorgbeard,由于您返回byte_list,请将其用于您的visit 调用,只更改原始代码中的一行:

            visit(file_dir, pass_test_func)
    

    到:

            byte_list += visit(file_dir, pass_test_func)
    

    这会创建额外的临时lists,但这通常没什么大不了的。

    【讨论】:

      猜你喜欢
      • 2021-05-16
      • 2017-07-08
      • 2021-12-11
      • 2020-02-06
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多