我发现它更方便访问字典键作为obj。foo而不是obj['foo'],所以我写了这个片段:

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

然而,我认为一定有一些原因,Python没有提供开箱即用的功能。以这种方式访问字典键的注意事项和缺陷是什么?


当前回答

使用SimpleNamespace:

from types import SimpleNamespace

obj = SimpleNamespace(color="blue", year=2050)

print(obj.color) #> "blue"
print(obj.year) #> 2050

编辑/更新:对OP的问题的更近的答案,从字典开始:

from types import SimpleNamespace

params = {"color":"blue", "year":2020}

obj = SimpleNamespace(**params)

print(obj.color) #> "blue"
print(obj.year) #> 2050

其他回答

这不是一个“好”的答案,但我认为这是俏皮的(它不处理嵌套字典在当前形式)。简单地将dict包装在函数中:

def make_funcdict(d=None, **kwargs)
    def funcdict(d=None, **kwargs):
        if d is not None:
            funcdict.__dict__.update(d)
        funcdict.__dict__.update(kwargs)
        return funcdict.__dict__
    funcdict(d, **kwargs)
    return funcdict

现在你的语法略有不同。访问dict项就像访问属性f.key一样。要以通常的方式访问dict项(和其他dict方法),请执行f()['key'],我们可以通过使用关键字参数和/或字典调用f来方便地更新dict

例子

d = {'name':'Henry', 'age':31}
d = make_funcdict(d)
>>> for key in d():
...     print key
... 
age
name
>>> print d.name
... Henry
>>> print d.age
... 31
>>> d({'Height':'5-11'}, Job='Carpenter')
... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'}

就是这样。如果有人提出这种方法的优点和缺点,我会很高兴。

product怎么样,我写了一个小小的Python类来统治它们:)

此外,您还可以获得自动代码完成、递归对象实例化和自动类型转换!

你完全可以做到你所要求的:

p = Prodict()
p.foo = 1
p.bar = "baz"

例1:类型提示

class Country(Prodict):
    name: str
    population: int

turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871

例2:自动类型转换

germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])

print(germany.population)  # 82175700
print(type(germany.population))  # <class 'int'>

print(germany.flag_colors)  # ['black', 'red', 'yellow']
print(type(germany.flag_colors))  # <class 'list'>

没有必要自己写 Setattr()和getattr()已经存在。

类对象的优势可能在类定义和继承中发挥作用。

更新- 2020年

自从这个问题在大约十年前被提出以来,Python本身已经发生了相当大的变化。

虽然我最初回答中的方法在某些情况下仍然有效,(例如,遗留项目坚持使用旧版本的Python,以及在某些情况下,您确实需要处理具有非常动态字符串键的字典),但我认为一般来说,Python 3.7中引入的数据类是AttrDict绝大多数用例的明显/正确的解决方案。

原来的答案

最好的方法是:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

一些优点:

它真的有用! 没有字典类方法被遮蔽(例如.keys()工作得很好。除非-当然-你给它们赋值,见下文) 属性和项总是同步的 试图将不存在的key作为属性访问会正确地引发AttributeError而不是KeyError 支持[Tab]自动补全(例如在jupyter和ipython中)

缺点:

如果.keys()等方法被传入的数据覆盖,它们就不能正常工作 在Python < 2.7.4 / Python3 < 3.2.3中导致内存泄漏 Pylint因为E1123(意外关键字参数)和E1103(可能没有成员)而抓狂 对于外行来说,这似乎是纯粹的魔法。

简要解释一下它是如何工作的

All python objects internally store their attributes in a dictionary that is named __dict__. There is no requirement that the internal dictionary __dict__ would need to be "just a plain dict", so we can assign any subclass of dict() to the internal dictionary. In our case we simply assign the AttrDict() instance we are instantiating (as we are in __init__). By calling super()'s __init__() method we made sure that it (already) behaves exactly like a dictionary, since that function calls all the dictionary instantiation code.

Python没有开箱即用提供此功能的原因之一

正如“cons”列表中所指出的,这将存储键的名称空间(可能来自任意和/或不受信任的数据!)与内置dict方法属性的名称空间结合在一起。例如:

d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items():    # TypeError: 'list' object is not callable
    print "Never reached!"

这并没有解决最初的问题,但是对于像我这样在这里寻找提供此功能的库的人来说应该是有用的。

Addict是一个很棒的库:https://github.com/mewwts/addict它照顾了前面的答案中提到的许多问题。

文档中的一个例子:

body = {
    'query': {
        'filtered': {
            'query': {
                'match': {'description': 'addictive'}
            },
            'filter': {
                'term': {'created_by': 'Mats'}
            }
        }
    }
}

成瘾者:

from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'