You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将计算文件夹信息的Python非尾递归函数转为非递归实现

问题描述

我写了一个递归函数dirSize,可以获取磁盘或指定文件夹的总大小、文件数及子目录数,实现简洁且运行正常:

# 将python的dirSize函数转为非递归版本:
from pathlib import Path

obj = {}
def dirSize(dir: Path, size = 0, files = 0, dirs = 0):
    for sub_dir in dir.glob('*'):
        if sub_dir.is_dir():
            dirs += 1
            res_size, res_files, res_dirs = dirSize(sub_dir)
            size += res_size
            files += res_files
            dirs += res_dirs
        elif sub_dir.is_file():
            files += 1
            size += sub_dir.stat().st_size
    
    obj[dir.as_posix()] = {
        'size': size,
        'files': files,
        'dirs': dirs,
    }
    
    return size, files, dirs

dirSize(Path('~/Documents').expanduser())
# 上面是测试代码,最终代码需要在管理员权限的cmd中运行:
#dirSize(Path('C:/'))

with open(Path('~/Desktop/results.tsv').expanduser().as_posix(), 'w') as file:
    file.write('Directory\tBytes\tFiles\tDirs\n')
    for key in sorted(obj):
        dir = obj[key]
        file.write(key)
        file.write('\t')
        file.write(str(dir['size']))
        file.write('\t')
        file.write(str(dir['files']))
        file.write('\t')
        file.write(str(dir['dirs']))
        file.write('\n')

但老板认为不是所有开发者都能熟练使用递归,要求改成非递归版本。我尝试用AI转换,但AI忽略了代码中size += res_size、files += res_files、dirs += res_dirs这几行的自底向上求和逻辑。之后我试过用栈循环(类似Dijkstra算法的思路),但没找到简单的自底向上求和方案;考虑过给obj变量添加层级/父/子属性,在队列处理完后再做自底向上求和,但这种方式太繁琐。有没有更简便的实现方法?

补充说明

  • 为何不用os.walk?
    os.walk虽然能直接消除递归,但无法实现自底向上求和,而我们需要的报告要展示每个目录的子目录数、磁盘占用空间及文件数。
  • 递归函数有什么问题?
    递归函数运行完全正常,只是老板要求提供非递归版本,所以我在纠结怎么写出简洁的非递归方案。

解决方案

可以用栈+状态标记的方式模拟递归的自底向上处理逻辑,核心思路是通过栈区分目录的「待处理子项」和「待汇总数据」两个阶段,先处理所有子目录/文件,再汇总父目录的统计数据,完美复刻原递归的逻辑:

from pathlib import Path

def non_recursive_dir_size(root_dir: Path):
    obj = {}
    # 栈元素:(目录路径, 是否已处理子项)
    stack = [(root_dir.expanduser(), False)]
    
    while stack:
        current_dir, is_processed = stack.pop()
        
        if not is_processed:
            # 第一次弹出,先标记为待汇总状态重新入栈
            stack.append((current_dir, True))
            # 逆序入栈所有子项(栈是后进先出,保证处理顺序和原递归一致)
            for item in reversed(list(current_dir.glob('*'))):
                if item.is_dir():
                    stack.append((item, False))
                else:
                    # 文件无后续子项,直接记录基础统计数据
                    obj[item.as_posix()] = {
                        'size': item.stat().st_size,
                        'files': 1,
                        'dirs': 0
                    }
        else:
            # 已处理完所有子项,开始汇总数据
            total_size = 0
            total_files = 0
            total_dirs = 0
            
            for item in current_dir.glob('*'):
                item_path = item.as_posix()
                if item.is_dir():
                    total_dirs += 1
                    total_size += obj[item_path]['size']
                    total_files += obj[item_path]['files']
                    total_dirs += obj[item_path]['dirs']
                else:
                    total_files += obj[item_path]['files']
                    total_size += obj[item_path]['size']
            
            obj[current_dir.as_posix()] = {
                'size': total_size,
                'files': total_files,
                'dirs': total_dirs
            }
    
    return obj

# 测试运行
result_obj = non_recursive_dir_size(Path('~/Documents'))
# 最终生产环境运行(需管理员权限):
# result_obj = non_recursive_dir_size(Path('C:/'))

# 写入TSV报告
with open(Path('~/Desktop/results.tsv').expanduser(), 'w') as file:
    file.write('Directory\tBytes\tFiles\tDirs\n')
    for dir_path in sorted(result_obj.keys()):
        stats = result_obj[dir_path]
        file.write(f"{dir_path}\t{stats['size']}\t{stats['files']}\t{stats['dirs']}\n")

代码说明

  1. 状态标记机制:通过is_processed标记,确保先处理所有子目录和文件,再回到父目录进行数据汇总,完全匹配原递归的自底向上逻辑
  2. 逆序入栈:利用栈的后进先出特性,逆序添加子项可以保证处理顺序和原递归一致,避免统计结果出现偏差
  3. 文件直接记录:文件没有子项,第一次遍历就写入统计数据,减少重复处理步骤
  4. 数据汇总逻辑:处理待汇总的目录时,遍历所有子项累加数据,和原递归中size += res_size等代码的逻辑完全对应

内容的提问来源于stack exchange,提问作者Marquinho Peli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:07:22