我如何使Python字典成员访问通过点“。”?

例如,我想写mydict.val而不是mydict['val']。

我还想以这种方式访问嵌套字典。例如

mydict.mydict2.val 

会提到

mydict = { 'mydict2': { 'val': ... } }

当前回答

如果你想pickle你修改后的字典,你需要添加几个状态方法到上面的答案:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self

其他回答

这是我从很久以前的一个项目里挖出来的。它可能还可以再优化一点,但就是这样了。

class DotNotation(dict):
    
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __init__(self, data):
        if isinstance(data, str):
            data = json.loads(data)
    
        for name, value in data.items():
            setattr(self, name, self._wrap(value))

    def __getattr__(self, attr):
        def _traverse(obj, attr):
            if self._is_indexable(obj):
                try:
                    return obj[int(attr)]
                except:
                    return None
            elif isinstance(obj, dict):
                return obj.get(attr, None)
            else:
                return attr
        if '.' in attr:
            return reduce(_traverse, attr.split('.'), self)
        return self.get(attr, None)

    def _wrap(self, value):
        if self._is_indexable(value):
            # (!) recursive (!)
            return type(value)([self._wrap(v) for v in value])
        elif isinstance(value, dict):
            return DotNotation(value)
        else:
            return value
    
    @staticmethod
    def _is_indexable(obj):
        return isinstance(obj, (tuple, list, set, frozenset))


if __name__ == "__main__":
    test_dict = {
        "dimensions": {
            "length": "112",
            "width": "103",
            "height": "42"
        },
        "meta_data": [
            {
                "id": 11089769,
                "key": "imported_gallery_files",
                "value": [
                    "https://example.com/wp-content/uploads/2019/09/unnamed-3.jpg",
                    "https://example.com/wp-content/uploads/2019/09/unnamed-2.jpg",
                    "https://example.com/wp-content/uploads/2019/09/unnamed-4.jpg"
                ]
            }
        ]
    }
    dotted_dict = DotNotation(test_dict)
    print(dotted_dict.dimensions.length) # => '112'
    print(getattr(dotted_dict, 'dimensions.length')) # => '112'
    print(dotted_dict.meta_data[0].key) # => 'imported_gallery_files'
    print(getattr(dotted_dict, 'meta_data.0.key')) # => 'imported_gallery_files'
    print(dotted_dict.meta_data[0].value) # => ['link1','link2','link2']
    print(getattr(dotted_dict, 'meta_data.0.value')) # => ['link1','link2','link3']
    print(dotted_dict.meta_data[0].value[2]) # => 'link3'
    print(getattr(dotted_dict, 'meta_data.0.value.2')) # => 'link3'

我的观点:出于我自己的目的,我开发了minydra,一个简单的命令行解析器,包括一个自定义类MinyDict(灵感来自addict):


In [1]: from minydra import MinyDict

In [2]: args = MinyDict({"foo": "bar", "yes.no.maybe": "idontknow"}).pretty_print(); args
╭──────────────────────────────╮
│ foo          : bar           │
│ yes.no.maybe : idontknow     │
╰──────────────────────────────╯
Out[2]: {'foo': 'bar', 'yes.no.maybe': 'idontknow'}

In [3]: args.resolve().pretty_print(); args
╭──────────────────────────╮
│ foo : bar                │
│ yes                      │
│ │no                      │
│ │ │maybe : idontknow     │
╰──────────────────────────╯
Out[3]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}

In [4]: args.yes.no.maybe
Out[4]: "idontknow"

In [5]: "foo" in args
Out[5]: True

In [6]: "rick" in args
Out[6]: False

In [7]: args.morty is None
Out[7]: True

In [8]: args.items()
Out[8]: dict_items([('foo', 'bar'), ('yes', {'no': {'maybe': 'idontknow'}})])

它通过向json yaml和pickle添加转储/加载方法来上瘾,并且在MinyDict.update()中也有一个严格的模式来防止创建新键(这对于防止命令行中的错字很有用)

最简单的解决方案。

定义一个只有pass语句的类。为该类创建对象并使用点表示法。

class my_dict:
    pass

person = my_dict()
person.id = 1 # create using dot notation
person.phone = 9999
del person.phone # Remove a property using dot notation

name_data = my_dict()
name_data.first_name = 'Arnold'
name_data.last_name = 'Schwarzenegger'

person.name = name_data
person.name.first_name # dot notation access for nested properties - gives Arnold

派生自dict和并实现__getattr__和__setattr__。

或者你也可以用Bunch,非常相似。

我不认为这是可能的monkeypatch内置字典类。

我只需要使用虚线路径字符串访问字典,所以我想到了:

def get_value_from_path(dictionary, parts):
    """ extracts a value from a dictionary using a dotted path string """

    if type(parts) is str:
        parts = parts.split('.')

    if len(parts) > 1:
        return get_value_from_path(dictionary[parts[0]], parts[1:])

    return dictionary[parts[0]]

a = {'a':{'b':'c'}}
print(get_value_from_path(a, 'a.b')) # c