在Python中将路径列表转换为字典

3

我正在使用 Python 编写一个程序,需要与“假设”的路径(即在实际文件系统中不存在的路径)进行交互,并且需要像正常情况下一样能够使用 listdir 列出它们的内容(例如path['directory'] 可以返回目录下的每个项,就像 os.listdir() 一样)。

我想出的解决方案是将字符串路径列表转换为一组字典。我编写了这个递归函数(它在一个类中):

    def DoMagic(self,paths):
        structure = {}
        if not type(paths) == list:
            raise ValueError('Expected list Value, not '+str(type(paths)))
        for i in paths:
            print(i)
            if i[0] == '/': #Sanity check
                print('trailing?',i) #Inform user that there *might* be an issue with the input.
                i[0] = ''
            i = i.split('/') #Split it, so that we can test against different parts.
            if len(i[1:]) > 1: #Hang-a-bout, there's more content!
                structure = {**structure, **self.DoMagic(['/'.join(i[1:])])}
            else:
                structure[i[1]] = i[1]

但是当我使用['foo/e.txt','foo/bar/a.txt','foo/bar/b.cfg','foo/bar/c/d.txt']作为输入运行时,会出现以下错误:

{'e.txt': 'e.txt', 'a.txt': 'a.txt', 'b.cfg': 'b.cfg', 'd.txt': 'd.txt'}

我希望只需键入“path [ 'foo'] [ 'bar']”,就可以获取“foo / bar /”目录中的所有内容。
编辑:
更理想的输出应为:
    {'foo':{'e.txt':'e.txt','bar':{'a.txt':'a.txt','c':{'d.txt':'d.txt'}}}}

1
你展示了你不想要的输出。请向我们展示期望的输出是什么。 - John Anderson
啊,我没意识到。抱歉。我现在已经添加了。 - ACBob
我现在明白你想做什么了。我建议创建一个Node类,其中包含一个children列表和一个type标志(filedirectory)。然后构建一个树形结构,模仿假设的目录结构,并编写一种查询该树的方法。 - John Anderson
2个回答

7

编辑 10-14-22 我的第一个答案符合OP的要求,但并不是最理想的方法,也不是最干净的输出。由于这个问题似乎更常用,请参见下面更清洁的方法,它对Unix/Windows路径更具弹性,输出字典更有意义。

from pathlib import Path
import json

def get_path_dict(paths: list[str | Path]) -> dict:
    """Builds a tree like structure out of a list of paths"""
    def _recurse(dic: dict, chain: tuple[str, ...] | list[str]):
        if len(chain) == 0:
            return
        if len(chain) == 1:
            dic[chain[0]] = None
            return
        key, *new_chain = chain
        if key not in dic:
            dic[key] = {}
        _recurse(dic[key], new_chain)
        return

    new_path_dict = {}
    for path in paths:
        _recurse(new_path_dict, Path(path).parts)
    return new_path_dict

l1 = ['foo/e.txt', 'foo/bar/a.txt', 'foo/bar/b.cfg', Path('foo/bar/c/d.txt'), 'test.txt']
result = get_path_dict(l1)
print(json.dumps(result, indent=2))

输出:

{
  "foo": {
    "e.txt": null,
    "bar": {
      "a.txt": null,
      "b.cfg": null,
      "c": {
        "d.txt": null
      }
    }
  },
  "test.txt": null
}

传统方法

这样做可以得到您想要的输出,但使用树结构可能更清晰。

from collections import defaultdict
import json

def nested_dict():
   """
   Creates a default dictionary where each value is an other default dictionary.
   """
   return defaultdict(nested_dict)

def default_to_regular(d):
    """
    Converts defaultdicts of defaultdicts to dict of dicts.
    """
    if isinstance(d, defaultdict):
        d = {k: default_to_regular(v) for k, v in d.items()}
    return d

def get_path_dict(paths):
    new_path_dict = nested_dict()
    for path in paths:
        parts = path.split('/')
        if parts:
            marcher = new_path_dict
            for key in parts[:-1]:
               marcher = marcher[key]
            marcher[parts[-1]] = parts[-1]
    return default_to_regular(new_path_dict)
            
l1 = ['foo/e.txt','foo/bar/a.txt','foo/bar/b.cfg','foo/bar/c/d.txt', 'test.txt']
result = get_path_dict(l1)
print(json.dumps(result, indent=2))

输出:

{
  "foo": {
    "e.txt": "e.txt",
    "bar": {
      "a.txt": "a.txt",
      "b.cfg": "b.cfg",
      "c": {
        "d.txt": "d.txt"
      }
    }
  },
  "test.txt": "test.txt"
}

非常优雅的解决方案。对我在其他方面有很大帮助。 - michal-ko
当我在我的路径列表上使用它时,它会抛出一个“TypeError:'str'对象不支持项目赋值”的错误。 - Yoda
@Yoda 如果我粘贴示例,它仍然可以工作。你输入了什么? - Error - Syntactical Remorse
@Yoda,虽然那段代码又旧又混乱,但我放了一个新的代码,应该会对你有所帮助。 - Error - Syntactical Remorse

1

pathlib如何帮助您处理不存在的路径? - Aran-Fey
简单的树,通过字典实现,不就足够了吗? - Piotr Rarus
1
@Aran-Fey 路径不必存在也可以使用 pathlib,当然除了检查/需要存在的方法。 - b_c
有“纯路径”和“具体路径”两种。 - Piotr Rarus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接