python中用点 "."来访问多层字典值

标题可能有点难以描述。举个例子。我有一个配置文件。

CONFIG = {
    'app_name': 'ZiXi',
    'mysql': {
        'host': '127.0.0.1',
    }
    'version': 'v2.0.11',
}

通常可以通过CONFIG['mysql']['host'] 这种方式访问,但我们还是想更简单点访问。如
CONFIG.mysql.host ,那么有几种方法。

1. 通过新定义的dict子类,实现getattr等

参考这里 https://stackoverflow.com/questions/2352181/how-to-use-a-dot-to-access-members-of-dictionary
这个类有个缺陷,就是不能访问多层的

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

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

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

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

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

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

使用示例:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

2. pip安装dotmap来使用

pip install dotmap

使用很简单:

data = DotMap(jsonDict)
print(data.location.city)

但是,也有一个缺陷,如果访问的是变量就跪了,作者说自己去搞go了,不再投入改这个。
像这样field 如果是'mysql.host'是不行的:

def sysconf(field):
    data = DotMap(jsonDict)
    return data.field

3. 通过自定义的方法实现

参考 https://vinta.ws/code/dot-notation-obj-x-y-z-for-nested-objects-and-dictionaries-in-python.html

import functools

def nested_dict_get(dictionary, dotted_key):
    keys = dotted_key.split('.')
    return functools.reduce(lambda d, key: d.get(key) if d else None, keys, dictionary)

user_dict = {
    'id': 123,
    'username': 'vinta',
    'settings': {
        'enable_nsfw': True,
    },
}

nested_dict_get(user_dict, 'username')  # return 'vinta'
nested_dict_get(user_dict, 'settings.enable_nsfw')  # return True
nested_dict_get(user_dict, 'settings.notification.new_follower')  # return None

对这个进行改良了下:

def sysconf(field):
    keys = field.split('.')
    return functools.reduce(lambda d, key: d.get(key) if d else None, keys, CONFIG)

上面CONFIG是我的字典形式的配置项, 通过 sysconf('mysql.host') 即可取到 CONFIG['mysql']['host']这个值了。