像属性一样访问字典键?

新手上路,请多包涵

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

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

但是,我认为 Python 没有提供开箱即用的功能肯定是有原因的。以这种方式访问 dict 键的注意事项和陷阱是什么?

原文由 Izz ad-Din Ruhulessin 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 644
1 个回答

更新 - 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() 工作得很好。除非 - 当然 - 你为它们分配了一些值,见下文)
  • 属性和项目始终同步
  • 尝试将不存在的密钥作为属性正确访问会引发 AttributeError 而不是 KeyError
  • 支持 [Tab] 自动完成(例如在 jupyter 和 ipython 中)

缺点:

  • .keys() 这样的方法如果被传入数据覆盖将 无法 正常工作
  • 在 Python < 2.7.4 / Python3 < 3.2.3 中导致 内存泄漏
  • Pylint 与 E1123(unexpected-keyword-arg)E1103(maybe-no-member)
  • 对于外行来说,这似乎是纯粹的魔法。

这是如何工作的简短解释

  • 所有 python 对象在内部将其属性存储在名为 __dict__ 的字典中。
  • 没有要求内部字典 __dict__ 需要“只是一个普通的字典”,所以我们可以将 dict() 的任何子类分配给内部字典。
  • 在我们的例子中,我们简单地分配我们正在实例化的 AttrDict() 实例(就像我们在 __init__ 中一样)。
  • 通过调用 super()__init__() 方法,我们确保它(已经)的行为与字典完全一样,因为该函数调用所有 字典实例化 代码。

Python 不提供开箱即用的此功能的一个原因

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

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

原文由 Kimvais 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏