问题 从列表os文件路径构建树(Python) - 依赖于性能


嘿,我正在研究一个用python编写的高性能文件管理/分析工具包。 我想创建一个函数,以树格式给我一个列表或类似的东西。 像这样的东西 问题(与java相关)

从:

dir/file
dir/dir2/file2
dir/file3
dir3/file4
dir3/file5

注意:路径列表未排序

至:

dir/
    file
    dir2/
        file2
    file3
dir3/
    file4
    file5

[[dir, [file, [dir2, [file2]], file3]], [dir3, [file4, file5]]]

这些方面的东西。我一直在玩一些想法,但没有一个能提供我想要的速度。

注意:我已经有了路径列表,所以不用担心。该函数采用路径列表并给出树列表。

提前致谢


9674
2017-12-13 05:55


起源

“我正在努力 非常高的性能 文件管理/分析工具包 用python编写。»......哎哟! :( - mac
哈哈,我知道,我想要一些合理的实现首先和后者我将使用更低级别的实现与cython等... - cwoebker


答案:


既然你已经澄清了这个问题,我想以下是你想要的:

from collections import defaultdict

input_ = '''dir/file
dir/dir2/file2
dir/file3
dir2/alpha/beta/gamma/delta
dir2/alpha/beta/gamma/delta/
dir3/file4
dir3/file5'''

FILE_MARKER = '<files>'

def attach(branch, trunk):
    '''
    Insert a branch of directories on its trunk.
    '''
    parts = branch.split('/', 1)
    if len(parts) == 1:  # branch is a file
        trunk[FILE_MARKER].append(parts[0])
    else:
        node, others = parts
        if node not in trunk:
            trunk[node] = defaultdict(dict, ((FILE_MARKER, []),))
        attach(others, trunk[node])

def prettify(d, indent=0):
    '''
    Print the file tree structure with proper indentation.
    '''
    for key, value in d.iteritems():
        if key == FILE_MARKER:
            if value:
                print '  ' * indent + str(value)
        else:
            print '  ' * indent + str(key)
            if isinstance(value, dict):
                prettify(value, indent+1)
            else:
                print '  ' * (indent+1) + str(value)



main_dict = defaultdict(dict, ((FILE_MARKER, []),))
for line in input_.split('\n'):
    attach(line, main_dict)

prettify(main_dict)

它输出:

dir3
  ['file4', 'file5']
dir2
  alpha
    beta
      gamma
        ['delta']
        delta
          ['']
dir
  dir2
    ['file2']
  ['file', 'file3']

有几点需要注意:

  • 该脚本大量使用 defaultdicts,基本上这允许跳过检查密钥的存在及其初始化(如果不存在)
  • 目录名称被映射到字典键,我认为这对您来说可能是一个很好的功能,因为密钥是经过哈希处理的,并且您可以比使用列表更快地检索信息。您可以访问表单中的层次结构 main_dict['dir2']['alpha']['beta']...
  • 注意区别 .../delta 和 .../delta/。我认为这有助于您快速区分您的叶子是目录还是文件。

我希望这回答了你的问题。如果有任何不清楚的地方,发表评论。


14
2017-12-13 22:03



看起来不错我会检查出来,告诉你它是否有效 - cwoebker
好的,谢谢这帮助很多,工作正常。我可能会改变索引文件的方式,所以我可以使基于它的所有内容更有效。但是非常感谢非常感谢! - cwoebker
@cwoebker - 没问题,快乐有帮助!祝你好运! - mac
感谢您提供代码/操作方法@mac,它对我很有用,但我希望我的树在每个级别按字母顺序排序。我发现了这个:[链接]stackoverflow.com/questions/21024969/... 但我不知道这棵树的工作原理。任何人都可以解释我如何正确使用'排序'功能或指向我一些'新手'课程。谢谢 ! - corentino


我不清楚你拥有什么和你需要什么(它可能有助于提供你所拥有的那些太慢的代码),但你可能应该将你的路径名分解为dirnames和basenames,然后构建一个使用特制类的树,或者至少是列表或字典的层次结构。然后,各种遍历应该允许您以几乎任何方式进行序列化。

至于性能问题,您是否考虑过使用Pypy,Cython或Shedskin?我有一个重复数据删除备份系统,我一直在努力,可以在Pypy或Cython上运行相同的代码;在Pypy上运行它实际上优于Cython增强版本(32位上的很多,64位上的一点点)。我也喜欢比较流行皮肤,但它显然无法穿过shedskin / cpython边界。

此外,当您遇到性能问题时,分析是必要的 - 至少,如果您已经选择了适当的算法。


1
2017-12-13 07:22



Cython计划在以后进行。 - cwoebker


首先, “非常高的表现” 和 “蟒蛇” 不要混合好。如果您正在寻找的是将性能优化到极致,那么切换到C将为您带来远远优于您可能想到的任何智能代码优化的好处。

其次, 很难相信这个瓶颈 在一个 “文件管理/分析工具包”  将是这个功能。磁盘上的I / O操作至少比内存中发生的任何操作慢几个数量级。分析你的代码是衡量这一点的唯一准确方法但是...如果我错了,我准备给你一个披萨! ;)

我建立了一个愚蠢的测试功能只是为了执行一些初步测量:

from timeit import Timer as T

PLIST = [['dir', ['file', ['dir2', ['file2']], 'file3']], ['dir3', ['file4', 'file5', 'file6', 'file7']]]

def tree(plist, indent=0):
    level = []
    for el in plist:
        if isinstance(el, list):
            level.extend(tree(el, indent + 2))
        else:
            level.append(' ' * indent + el)
    return level

print T(lambda : tree(PLIST)).repeat(number=100000)

这输出:

[1.0135619640350342, 1.0107290744781494, 1.0090651512145996]

由于测试路径列表是10个文件,并且迭代次数是100000,这意味着在1秒内您可以处理大约100万个文件的树。现在......除非你在Google工作,否则这对我来说似乎是可以接受的。

相比之下,当我开始写这个答案时,我点击了我的主要80Gb HD的根目录上的“属性”选项[这应该是给我我使用C代码的文件数量]。几分钟过去了,我大约50 GB,300000个文件......

HTH! :)


0
2017-12-13 07:56



这确实非常快,但与我的问题无关,我需要用正则表达式或其他东西分析我的所有路径,然后得到一个像你输入中使用的树结构 - cwoebker
@cwoebker - 你应该澄清一下你的问题,就像现在一样,你不清楚你的输入和预期输出是什么。它也有助于知道您是否可以访问文件系统并且可以从中读取文件名,或者如果您必须使用输入,那么您希望能够举例说明。 :) - mac
我添加了示例输入...我有权访问文件系统。在项目中我有一个redis实例中的文件索引,我从那里读取路径,所以我不想再次访问文件 - cwoebker
我想在谈论python中的高性能时提到切换到c是陈词滥调。大家都知道,但你没有看到每个人都回到c。每种语言都有自己的目的,并且非常清楚OP意味着最快的算法,即在Python中实现的最少时间顺序。 - Ishan Srivastava