【问题标题】:How to iterate over nested dictionary structure and operate on each string?如何迭代嵌套字典结构并对每个字符串进行操作?
【发布时间】:2018-11-12 07:19:45
【问题描述】:

我有一个字典,它可以包含任意顺序的字符串、字符串列表或最终以字符串结尾的嵌套字典。我想遍历这个字典并对每个字符串执行一个操作。

This 问题与我正在寻找的问题很接近,但我未能成功地将该解决方案应用于我自己的问题。

我需要将函数os.path.expanduser() 应用于以下字典中的每个字符串:

x = dict(
    dir = dict(
        wd = '~/Desktop/WD',
        pymodule = [
            '~/Documents/PythonModule',
            '/Users/Username/Documents/PythonModule2'
        ],
        album = '~/Desktop/Album'
    ),
    file = dict(
        XML = '~/Downloads/data.xml',
        CSV = '~/Downloads/data.csv'
    )
)

理想情况下,我想定义一个类,当在普通字典上调用时,将在该字典的每个字符串元素上应用 os.path.expanduser()

class MyDict:
    def __init__(d):
        self.d = d
        # some operation to apply os.path.expanduser() on each string element of 'd'

我怎样才能做到这一点?

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

    这可以通过递归函数轻松完成。让我们看一个示例实现。在这里,我们将给定容器中的所有字符串映射到给定函数,我们还将使用List ComprehensionsDictionary Comprehensions 来模仿原始嵌套结构。另外,isinstance 内置函数用于检查给定参数的类型:

    def convert(func, d):
      if (isinstance(d, str)):
        return func(d)
      elif (isinstance(d, dict)):
        return {key : convert(func, d[key]) for key in d}
      elif (isinstance(d, list)):
        return [convert(func, val) for val in d]
    

    func 应用于容器中的每个字符串。使用您的示例字典和os.path.expanduser 对其进行测试:

    x = dict(
        dir = dict(
            wd = '~/Desktop/WD',
            pymodule = [
                '~/Documents/PythonModule',
                '/Users/Username/Documents/PythonModule2'
            ],
            album = '~/Desktop/Album'
        ),
        file = dict(
            XML = '~/Downloads/data.xml',
            CSV = '~/Downloads/data.csv'
        )
    )
    
    
    import os
    x = convert(os.path.expanduser, x)
    print(x)
    

    果然你得到了想要的输出:

    {'dir': {'wd': '/home/runner/Desktop/WD', 'pymodule': ['/home/runner/Documents/PythonModule', '/Users/Username/Documents/PythonModule2'], 'album': '/home/runner/Desktop/Album'}, 'file': {'XML': '/home/runner/Downloads/data.xml', 'CSV': '/home/runner/Downloads/data.csv'}}
    

    【讨论】:

    • 很好的答案,非常感谢您将convert() 函数推广到接受任何输入函数,而不仅仅是os.path.expanduser()
    【解决方案2】:

    这是一个函数,它接受一个嵌套结构 x 作为输入,并返回一个类似的嵌套结构,其中所有字符串都已展开:

    def expand(x):
        if isinstance(x, str):
            return os.path.expanduser(x)
        if isinstance(x, dict):
            return { key : expand(x[key]) for key in x }
        if isinstance(x, list):
            return [ expand(elem) for elem in x ]
        return x
    

    例如调用它

    expand({1: '~/Hello', 2: ['~/World', '~/foo']})
    

    会回来

    {1: '/home/hkoehler/Hello', 2: ['/home/hkoehler/World', '/home/hkoehler/foo']}
    

    【讨论】:

    • 虽然这可能会回答作者的问题,但它缺少一些解释性文字和/或文档链接。如果没有围绕它们的一些短语,原始代码 sn-ps 并不是很有帮助。您可能还会发现how to write a good answer 非常有帮助。请编辑您的答案。
    • 这是一个很棒的答案,解决了我在原始问题中的所有担忧。它适用于任意深度的字典,这正是我的想法。谢谢!
    【解决方案3】:

    这是一个可以做到这一点的函数:

    import json
    import os
    
    x = dict(
        dir = dict(
            wd = '~/Desktop/WD',
            pymodule = [
                '~/Documents/PythonModule',
                '/Users/Username/Documents/PythonModule2'
            ],
            album = '~/Desktop/Album'
        ),
        file = dict(
            XML = '~/Downloads/data.xml',
            CSV = '~/Downloads/data.csv'
        )
    )
    
    def func(d):
        for key, value in d.items():
            if isinstance(value, dict):
                func(value)
            elif isinstance(value, str):
                d[key] = os.path.expanduser(value)
            elif isinstance(value, list):
                for i, element in enumerate(value):
                    if isinstance(element, str):
                        value[i] = os.path.expanduser(element)
    
    func(x)
    print(json.dumps(x, indent=4))
    

    输出:

    {
        "dir": {
            "wd": "C:\\Users\\martineau/Desktop/WD",
            "pymodule": [
                "C:\\Users\\martineau/Documents/PythonModule",
                "/Users/Username/Documents/PythonModule2"
            ],
            "album": "C:\\Users\\martineau/Desktop/Album"
        },
        "file": {
            "XML": "C:\\Users\\martineau/Downloads/data.xml",
            "CSV": "C:\\Users\\martineau/Downloads/data.csv"
        }
    }
    

    【讨论】:

    • 这不会替换嵌套在列表深处的字符串。例如。 [ [ '~/foo' ] ].
    • @HenningKoehler:所以? OP 的数据结构中没有这样的东西。
    猜你喜欢
    • 2020-01-05
    • 1970-01-01
    • 1970-01-01
    • 2017-07-15
    • 2021-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-24
    相关资源
    最近更新 更多