回答提出的问题
为什么Python不直接提供这个功能?
我猜测这可能与Python之禅有关:“做一件事应该只有一种 -- 最好是唯一一种 -- 显而易见的方法。” 这样会产生两种明显的从字典中获取值的方式:obj['key']
和 obj.key
。
注意事项和陷阱
这些包括可能在代码中缺乏清晰度和混淆。也就是说,以下代码对于稍后维护您的代码的其他人甚至对于您自己,在一段时间内不再返回代码时也可能会感到困惑。同样来自Python之禅:“可读性很重要!”
>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
如果实例化了
d
或定义了
KEY
或离开
d.spam
使用的地方分配了
d[KEY]
,很容易导致混淆,因为这不是常用的习惯用法。我知道这可能会让我感到困惑。
此外,如果您按以下方式更改
KEY
的值(但忘记更改
d.spam
),则现在会得到:
>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'
在我看来,这并不值得花费努力。
其他项
正如其他人所指出的那样,您可以使用任何可哈希对象(不仅仅是字符串)作为字典键。例如,
>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>>
是合法的,但是
>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
File "<stdin>", line 1
d.(2, 3)
^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>>
这让你可以访问整个可打印字符范围或其他可哈希对象作为字典键,而在访问对象属性时则没有这种功能。这使得像缓存对象元类这样的魔法成为可能,就像
Python Cookbook(第9章)中的配方一样。
在我的编辑中,我更喜欢
spam.eggs
的美学风格,因为我认为它看起来更干净,并且当我遇到
namedtuple
时,我真的开始渴望这种功能。但是以下便利性胜过了它。
>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>
这是一个简单的例子,但我经常发现自己在不同情况下使用字典而不是obj.key
表示法(例如,当我需要从XML文件中读取首选项时)。在其他情况下,当我因为美观原因想要实例化一个动态类并将某些属性添加到它上面时,我继续使用字典以保持一致性以提高可读性。
我确信OP已经满意地解决了这个问题,但如果他仍然想要这个功能,则建议他从pypi下载提供此功能的软件包之一:
Bunch 是我更熟悉的一个。它是 dict
的子类,因此具有所有的功能。
AttrDict 看起来也很不错,但我不太熟悉它,也没有像 Bunch 那样详细查看源代码。
- Addict 正在积极维护,并提供类似属性的访问和其他功能。
- 正如 Rotareti 在评论中指出的那样,Bunch 已被弃用,但有一个活跃的分支叫做 Munch。
然而,为了提高代码的可读性,我强烈建议他不要混合使用不同的符号风格。如果他偏爱这种符号风格,那么他应该只需实例化一个动态对象,将所需的属性添加到其中,然后完成即可:
>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}
在此我更新,以回答评论中的后续问题
在下面的评论中,Elmo 问道:
如果你想再深入一层怎么办?(指 type(...))
虽然我从未使用过这个用例(再次强调,我倾向于使用嵌套的 dict
,以保持一致性),但以下代码可行:
>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
... setattr(d, x, C())
... i = 1
... for y in 'one two three'.split():
... setattr(getattr(d, x), y, i)
... i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}
collections.namedtuple
对此非常有用。 - user395760False
的异常。这样,像if (not) dict.key:
这样的检查就可以起作用了。 - Marki