32

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

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 和类似的。)

4

5 回答 5

55

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

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': {}}}
于 2010-06-13T06:08:30.603 回答
6

下面是嵌套属性字典的另一个实现(受 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': {}}}

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

hasattr(life, 'parallelUniverse')
# --> False
于 2018-02-15T11:52:01.117 回答
4

有一个包可以做你想要的,还有更多的东西,它叫做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:我是Prodict的作者。

于 2020-02-20T09:08:32.467 回答
1

这是另一个解决方案:

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

打印正确的值

于 2018-11-17T18:45:52.433 回答
0
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_

# Author: Xingbang Jiang
# E-mail: 1278561590@qq.com
# 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))
于 2019-03-28T05:28:31.980 回答