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

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

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

mydict.mydict2.val 

会提到

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

当前回答

如果你已经在使用pandas,你可以构造一个pandas Series或DataFrame,从中你可以通过点语法访问项目:

1级字典:

import pandas as pd

my_dictionary = pd.Series({
  'key1': 'value1',
  'key2': 'value2'
})

print(my_dictionary.key1)
# Output: value1

2级字典:

import pandas as pd

my_dictionary = pd.DataFrame({
  'key1': {
    'inner_key1': 'value1'
  },
  'key2': {
    'inner_key2': 'value2'
  }
})

print(my_dictionary.key1.inner_key1)
# Output: value1

请注意,这可能在规范化数据结构(其中每个字典条目都具有相同的结构)下工作得更好。在上面的第二个例子中,得到的DataFrame是:

              key1    key2
inner_key1  value1     NaN
inner_key2     NaN  value2

其他回答

此解决方案是对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.

你可以用我刚做的这个类来做。对于这个类,您可以像使用另一个字典(包括json序列化)一样使用Map对象,或者使用点表示法。希望对大家有所帮助:

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']

我试了一下:

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

你也可以尝试__getattribute__。

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

我不喜欢在(超过)10年前的火灾中添加另一个日志,但我也会检查dotwiz库,它是我最近发布的——实际上就在今年。

它是一个相对较小的库,在基准测试中,它在get(访问)和设置(创建)时间方面也表现得非常好,至少与其他备选方案相比是这样。

通过pip安装dotwiz

pip install dotwiz

它能做你想让它做的所有事情,并继承dict的子类,所以它的操作就像一个普通的字典:

from dotwiz import DotWiz

dw = DotWiz()
dw.hello = 'world'
dw.hello
dw.hello += '!'
# dw.hello and dw['hello'] now both return 'world!'
dw.val = 5
dw.val2 = 'Sam'

最重要的是,你可以将它转换为dict对象:

d = dw.to_dict()
dw = DotWiz(d) # automatic conversion in constructor

这意味着如果你想访问的东西已经是dict形式的,你可以把它变成一个dotwz来方便访问:

import json
json_dict = json.loads(text)
data = DotWiz(json_dict)
print data.location.city

最后,我正在做的一些令人兴奋的事情是一个现有的特性请求,这样它就会自动创建新的子DotWiz实例,这样你就可以做这样的事情:

dw = DotWiz()
dw['people.steve.age'] = 31

dw
# ✫(people=✫(steve=✫(age=31)))

与点图比较

我在下面添加了一个快速而粗略的性能比较。

首先,用pip安装两个库:

pip install dotwiz dotmap

为了进行基准测试,我编写了以下代码:

from timeit import timeit

from dotwiz import DotWiz
from dotmap import DotMap


d = {'hey': {'so': [{'this': {'is': {'pretty': {'cool': True}}}}]}}

dw = DotWiz(d)
# ✫(hey=✫(so=[✫(this=✫(is=✫(pretty={'cool'})))]))

dm = DotMap(d)
# DotMap(hey=DotMap(so=[DotMap(this=DotMap(is=DotMap(pretty={'cool'})))]))

assert dw.hey.so[0].this['is'].pretty.cool == dm.hey.so[0].this['is'].pretty.cool

n = 100_000

print('dotwiz (create):  ', round(timeit('DotWiz(d)', number=n, globals=globals()), 3))
print('dotmap (create):  ', round(timeit('DotMap(d)', number=n, globals=globals()), 3))
print('dotwiz (get):  ', round(timeit("dw.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))
print('dotmap (get):  ', round(timeit("dm.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))

结果,在我的M1 Mac上运行Python 3.10:

dotwiz (create):   0.189
dotmap (create):   1.085
dotwiz (get):   0.014
dotmap (get):   0.335

@derek73的答案非常简洁,但它不能被pickle或(深度)复制,并且它在缺少键时返回None。下面的代码修复了这个问题。

编辑:我没有看到上面的答案解决了完全相同的问题(点赞)。我把答案留在这里供参考。

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

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)