通过属性递归访问字典以及索引访问?

发布于 2024-09-05 15:24:14 字数 633 浏览 10 评论 0原文

我希望能够做这样的事情:

from dotDict import dotdictify

life = {'bigBang':
           {'stars':
               {'planets': []}
           }
       }

dotdictify(life)

# This would be the regular way:
life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}}
# But how can we make this work?
life.bigBang.stars.planets.earth = {'singleCellLife': {}}

#Also creating new child objects if none exist, using the following syntax:
life.bigBang.stars.planets.earth.multiCellLife = {'reptiles':{},'mammals':{}}

我的动机是提高代码的简洁性,如果可能的话,使用与 Javascript 类似的语法来访问 JSON 对象,以实现高效的跨平台开发。 (我也使用 Py2JS 和类似的。)

I'd like to be able to do something like this:

from dotDict import dotdictify

life = {'bigBang':
           {'stars':
               {'planets': []}
           }
       }

dotdictify(life)

# This would be the regular way:
life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}}
# But how can we make this work?
life.bigBang.stars.planets.earth = {'singleCellLife': {}}

#Also creating new child objects if none exist, using the following syntax:
life.bigBang.stars.planets.earth.multiCellLife = {'reptiles':{},'mammals':{}}

My motivations are to improve the succinctness of the code, and if possible use similar syntax to Javascript for accessing JSON objects for efficient cross platform development. (I also use Py2JS and similar.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

万人眼中万个我 2024-09-12 15:24:14

以下是创造这种体验的一种方法:

class DotDictify(dict):
    MARKER = object()

    def __init__(self, value=None):
        if value is None:
            pass
        elif isinstance(value, dict):
            for key in value:
                self.__setitem__(key, value[key])
        else:
            raise TypeError('expected dict')

    def __setitem__(self, key, value):
        if isinstance(value, dict) and not isinstance(value, DotDictify):
            value = DotDictify(value)
        super(DotDictify, self).__setitem__(key, value)

    def __getitem__(self, key):
        found = self.get(key, DotDictify.MARKER)
        if found is DotDictify.MARKER:
            found = DotDictify()
            super(DotDictify, self).__setitem__(key, found)
        return found

    __setattr__, __getattr__ = __setitem__, __getitem__


if __name__ == '__main__':

    life = {'bigBang':
               {'stars':
                   {'planets': {}  # Value changed from []
                   }
               }
           }

    life = DotDictify(life)
    print(life.bigBang.stars.planets)  # -> []
    life.bigBang.stars.planets.earth = {'singleCellLife' : {}}
    print(life.bigBang.stars.planets)  # -> {'earth': {'singleCellLife': {}}}

Here's one way to create that kind of experience:

class DotDictify(dict):
    MARKER = object()

    def __init__(self, value=None):
        if value is None:
            pass
        elif isinstance(value, dict):
            for key in value:
                self.__setitem__(key, value[key])
        else:
            raise TypeError('expected dict')

    def __setitem__(self, key, value):
        if isinstance(value, dict) and not isinstance(value, DotDictify):
            value = DotDictify(value)
        super(DotDictify, self).__setitem__(key, value)

    def __getitem__(self, key):
        found = self.get(key, DotDictify.MARKER)
        if found is DotDictify.MARKER:
            found = DotDictify()
            super(DotDictify, self).__setitem__(key, found)
        return found

    __setattr__, __getattr__ = __setitem__, __getitem__


if __name__ == '__main__':

    life = {'bigBang':
               {'stars':
                   {'planets': {}  # Value changed from []
                   }
               }
           }

    life = DotDictify(life)
    print(life.bigBang.stars.planets)  # -> []
    life.bigBang.stars.planets.earth = {'singleCellLife' : {}}
    print(life.bigBang.stars.planets)  # -> {'earth': {'singleCellLife': {}}}
樱花坊 2024-09-12 15:24:14

下面是嵌套属性字典的另一个实现(受到 Curt Hagenlocher 答案的启发,精简到必要的部分):

class AttrDict(dict):
    """ Nested Attribute Dictionary

    A class to convert a nested Dictionary into an object with key-values
    accessible using attribute notation (AttrDict.attribute) in addition to
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse into nested dicts (like: AttrDict.attr.attr)
    """

    def __init__(self, mapping=None):
        super(AttrDict, self).__init__()
        if mapping is not None:
            for key, value in mapping.items():
                self.__setitem__(key, value)

    def __setitem__(self, key, value):
        if isinstance(value, dict):
            value = AttrDict(value)
        super(AttrDict, self).__setitem__(key, value)
        self.__dict__[key] = value  # for code completion in editors

    def __getattr__(self, item):
        try:
            return self.__getitem__(item)
        except KeyError:
            raise AttributeError(item)

    __setattr__ = __setitem__

这在 Python 2 和 3 中都有效:

life = AttrDict({'bigBang': {'stars': {'planets': {}}}})
life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}}
life.bigBang.stars.planets.earth.multiCellLife = {'reptiles': {}, 'mammals': {}}
print(life.bigBang.stars.planets.earth)
# -> {'singleCellLife': {}, 'multiCellLife': {'mammals': {}, 'reptiles': {}}}

在 Python3 中需要将 KeyError 转换为 __getattr__ 中的 AttributeError这样 hasattr 在找不到属性的情况下也可以工作:

hasattr(life, 'parallelUniverse')
# --> False

Below another implementation of a nested attribute dictionary (inspired by the answer of Curt Hagenlocher, stripped down to the essential):

class AttrDict(dict):
    """ Nested Attribute Dictionary

    A class to convert a nested Dictionary into an object with key-values
    accessible using attribute notation (AttrDict.attribute) in addition to
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse into nested dicts (like: AttrDict.attr.attr)
    """

    def __init__(self, mapping=None):
        super(AttrDict, self).__init__()
        if mapping is not None:
            for key, value in mapping.items():
                self.__setitem__(key, value)

    def __setitem__(self, key, value):
        if isinstance(value, dict):
            value = AttrDict(value)
        super(AttrDict, self).__setitem__(key, value)
        self.__dict__[key] = value  # for code completion in editors

    def __getattr__(self, item):
        try:
            return self.__getitem__(item)
        except KeyError:
            raise AttributeError(item)

    __setattr__ = __setitem__

This works in both Python 2 and 3:

life = AttrDict({'bigBang': {'stars': {'planets': {}}}})
life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}}
life.bigBang.stars.planets.earth.multiCellLife = {'reptiles': {}, 'mammals': {}}
print(life.bigBang.stars.planets.earth)
# -> {'singleCellLife': {}, 'multiCellLife': {'mammals': {}, 'reptiles': {}}}

Converting KeyError into AttributeError in __getattr__ is required in Python3 such that hasattr works also in case the attribute is not found:

hasattr(life, 'parallelUniverse')
# --> False
○闲身 2024-09-12 15:24:14

有一个包可以完全满足您的需求,而且还可以做更多的事情,它被称为 Prodict

from prodict import Prodict

life_dict = {'bigBang':
                {'stars':
                    {'planets': []}
                }
            }

life = Prodict.from_dict(life_dict)

print(life.bigBang.stars.planets)
# prints []

# you can even add new properties dynamically
life.bigBang.galaxies = []

PS:我是该产品的作者。

There is a package doing exactly what you want and also something more and it is called Prodict.

from prodict import Prodict

life_dict = {'bigBang':
                {'stars':
                    {'planets': []}
                }
            }

life = Prodict.from_dict(life_dict)

print(life.bigBang.stars.planets)
# prints []

# you can even add new properties dynamically
life.bigBang.galaxies = []

PS: I'm the author of the Prodict.

迎风吟唱 2024-09-12 15:24:14

这是另一个解决方案:

from typing import Dict, Any

class PropertyTree: pass

def dict_to_prop_tree(yaml_config: Dict[str, Any]) -> PropertyTree:
    tree = PropertyTree()
    for key, value in yaml_config.items():
        if type(value) == dict:
            setattr(tree, key, dict_to_obj_tree(value))
        elif type(value) == list:
            setattr(tree, key, [dict_to_obj_tree(v) for v in value])
        else:
            setattr(tree, key, value)
    return tree

然后在 python 控制台中:

d={'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5, 'f': {'g': 6}, 'h': {}, 'j': 7}}
tree=dict_to_prop_tree(d)
tree.a
tree.c.f.g

打印正确的值

Here is another solution:

from typing import Dict, Any

class PropertyTree: pass

def dict_to_prop_tree(yaml_config: Dict[str, Any]) -> PropertyTree:
    tree = PropertyTree()
    for key, value in yaml_config.items():
        if type(value) == dict:
            setattr(tree, key, dict_to_obj_tree(value))
        elif type(value) == list:
            setattr(tree, key, [dict_to_obj_tree(v) for v in value])
        else:
            setattr(tree, key, value)
    return tree

Then in the python console:

d={'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5, 'f': {'g': 6}, 'h': {}, 'j': 7}}
tree=dict_to_prop_tree(d)
tree.a
tree.c.f.g

prints the correct values

山田美奈子 2024-09-12 15:24:14
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_

# Author: Xingbang Jiang
# E-mail: [email protected]
# HomePage: http://www.xingbangsharing.tech

class Dotsdict(dict):
    def __init__(self, args, **kwargs):
        super(Dotsdict, self).__init__(args, **kwargs)
        for obj in [args, kwargs]:
            for k, v in obj.items():
                if isinstance(v, dict):
                    v = Dotsdict(v)
                self.__setitem__(k, v)

    def __setitem__(self, key, val):
        super(Dotsdict, self).__setitem__(key, val)
        # self.__dict__[key] = val

    def __delitem__(self, key):
        super(Dotsdict, self).__delitem__(key)
        # del self.__dict__[key]

    def __getitem__(self, key):
        return super(Dotsdict, self).__getitem__(key)

    def __missing__(self, key):
        dots = Dotsdict()
        self.__setitem__(key, dots)
        return dots

    __setattr__, __delattr__, __getattr__ = __setitem__, __delitem__, __getitem__

# ===================================================================


d = {'k': 'v', 'x': {'y': 'z', 'p': 'q', }, }
print(type(d))
print(d)

dd = Dotsdict(d, i='j')
print(type(dd))
print(dd)

print('========================================')

dd.a = 'b'
dd.x.m = 'n'
print(dd.x.y)

del dd.x['p']
print(dd)
print(len(dd))
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_

# Author: Xingbang Jiang
# E-mail: [email protected]
# HomePage: http://www.xingbangsharing.tech

class Dotsdict(dict):
    def __init__(self, args, **kwargs):
        super(Dotsdict, self).__init__(args, **kwargs)
        for obj in [args, kwargs]:
            for k, v in obj.items():
                if isinstance(v, dict):
                    v = Dotsdict(v)
                self.__setitem__(k, v)

    def __setitem__(self, key, val):
        super(Dotsdict, self).__setitem__(key, val)
        # self.__dict__[key] = val

    def __delitem__(self, key):
        super(Dotsdict, self).__delitem__(key)
        # del self.__dict__[key]

    def __getitem__(self, key):
        return super(Dotsdict, self).__getitem__(key)

    def __missing__(self, key):
        dots = Dotsdict()
        self.__setitem__(key, dots)
        return dots

    __setattr__, __delattr__, __getattr__ = __setitem__, __delitem__, __getitem__

# ===================================================================


d = {'k': 'v', 'x': {'y': 'z', 'p': 'q', }, }
print(type(d))
print(d)

dd = Dotsdict(d, i='j')
print(type(dd))
print(dd)

print('========================================')

dd.a = 'b'
dd.x.m = 'n'
print(dd.x.y)

del dd.x['p']
print(dd)
print(len(dd))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文