我如何使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

其他回答

此解决方案是对epool提供的解决方案的改进,以满足OP以一致的方式访问嵌套字典的需求。epool的解决方案不允许访问嵌套字典。

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

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

使用这个类,现在可以执行如下操作:A.B.C.D.

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

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'

kaggle_environments使用的实现是一个名为structify的函数。

class Struct(dict):
    def __init__(self, **entries):
        entries = {k: v for k, v in entries.items() if k != "items"}
        dict.__init__(self, entries)
        self.__dict__.update(entries)

    def __setattr__(self, attr, value):
        self.__dict__[attr] = value
        self[attr] = value

# Added benefit of cloning lists and dicts.
def structify(o):
    if isinstance(o, list):
        return [structify(o[i]) for i in range(len(o))]
    elif isinstance(o, dict):
        return Struct(**{k: structify(v) for k, v in o.items()})
    return o

https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/utils.py

这可能有助于在《ConnectX》等游戏中测试AI模拟代理

from kaggle_environments import structify

obs  = structify({ 'remainingOverageTime': 60, 'step': 0, 'mark': 1, 'board': [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]})
conf = structify({ 'timeout': 2, 'actTimeout': 2, 'agentTimeout': 60, 'episodeSteps': 1000, 'runTimeout': 1200, 'columns': 7, 'rows': 6, 'inarow': 4, '__raw_path__': '/kaggle_simulations/agent/main.py' })

def agent(obs, conf):
  action = obs.step % conf.columns
  return action

我试了一下:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

你也可以尝试__getattribute__。

使每个字典都是一种类型的dotdict就足够了,如果你想从多层字典初始化它,也可以尝试实现__init__。

不喜欢。在Python中,属性访问和索引是分开的事情,您不应该希望它们执行相同的操作。创建一个类(可能是由namedtuple创建的),如果你有一些应该具有可访问属性的东西,并使用[]符号从字典中获取一个项。