问题描述
我发现以 obj.foo
而不是 obj['foo']
访问字典键更方便,所以我写了这个片段:
I find it more convenient to access dict keys as obj.foo
instead of obj['foo']
, so I wrote this snippet:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
然而,我认为一定有某种原因,Python 没有提供这种开箱即用的功能.以这种方式访问 dict 键有哪些注意事项和陷阱?
However, I assume that there must be some reason that Python doesn't provide this functionality out of the box. What would be the caveats and pitfalls of accessing dict keys in this manner?
推荐答案
更新 - 2020
自从大约十年前提出这个问题以来,Python 本身发生了相当大的变化.
Update - 2020
Since this question was asked almost ten years ago, quite a bit has changed in Python itself since then.
虽然我的原始答案中的方法在某些情况下仍然有效(例如,遗留项目坚持使用旧版本的 Python 以及您确实需要使用非常动态的字符串键处理字典的情况),但我认为一般来说 dataclasses 是绝大多数用例的明显/正确的解决方案AttrDict
.
While the approach in my original answer is still valid for some cases, (e.g. legacy projects stuck to older versions of Python and cases where you really need to handle dictionaries with very dynamic string keys), I think that in general the dataclasses introduced in Python 3.7 are the obvious/correct solution to vast majority of the use cases of AttrDict
.
最好的方法是:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
一些优点:
- 它确实有效!
- 没有隐藏字典类方法(例如
.keys()
工作得很好.除非 - 当然 - 你给它们分配一些值,见下文) - 属性和项目始终同步
- 尝试访问不存在的键作为属性正确引发
AttributeError
而不是KeyError
- 支持 [Tab] 自动完成(例如在 jupyter 和 ipython 中)
- It actually works!
- No dictionary class methods are shadowed (e.g.
.keys()
work just fine. Unless - of course - you assign some value to them, see below) - Attributes and items are always in sync
- Trying to access non-existent key as an attribute correctly raises
AttributeError
instead ofKeyError
- Supports [Tab] autocompletion (e.g. in jupyter & ipython)
缺点:
- 如果
.keys()
之类的方法被传入数据覆盖,它们将不能正常工作 - 在 Python 中导致 内存泄漏 2.7.4/Python3
- Pylint 使用
E1123(unexpected-keyword-arg)
和E1103(maybe-no-member)
- 对于没有经验的人来说,这似乎是纯粹的魔法.
- Methods like
.keys()
will not work just fine if they get overwritten by incoming data - Causes a memory leak in Python < 2.7.4 / Python3 < 3.2.3
- Pylint goes bananas with
E1123(unexpected-keyword-arg)
andE1103(maybe-no-member)
- For the uninitiated it seems like pure magic.
- 所有 python 对象在内部将它们的属性存储在一个名为
__dict__
的字典中. - 没有要求内部字典
__dict__
需要只是一个普通的 dict",因此我们可以将dict()
的任何子类分配给内部字典. - 在我们的例子中,我们只是简单地分配我们正在实例化的
AttrDict()
实例(就像我们在__init__
中一样). - 通过调用
super()
的__init__()
方法,我们确保它(已经)表现得与字典完全一样,因为该函数调用了所有字典实例化代码.
- 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 ofdict()
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.
如缺点"中所述列表,这结合了存储键的命名空间(可能来自任意和/或不受信任的数据!)与内置 dict 方法属性的命名空间.例如:
d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items(): # TypeError: 'list' object is not callable
print "Never reached!"
这篇关于像属性一样访问字典键?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!