我发现它更方便访问字典键作为obj。foo而不是obj['foo'],所以我写了这个片段:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
然而,我认为一定有一些原因,Python没有提供开箱即用的功能。以这种方式访问字典键的注意事项和缺陷是什么?
以这种方式访问字典键的注意事项和缺陷是什么?
正如@Henry所指出的,在dict中不能使用点访问的一个原因是,它将dict键名限制为python有效变量,从而限制了所有可能的名称。
下面是一些例子,说明为什么在给定字典d的情况下,点点访问通常没有帮助:
有效性
以下属性在Python中是无效的:
d.1_foo # enumerated names
d./bar # path names
d.21.7, d.12:30 # decimals, time
d."" # empty strings
d.john doe, d.denny's # spaces, misc punctuation
d.3 * x # expressions
风格
PEP8约定将对属性命名施加软约束:
A.保留关键字(或内置函数)名称:
d.in
d.False, d.True
d.max, d.min
d.sum
d.id
如果函数参数的名称与保留关键字冲突,通常最好在后面添加一个下划线…
B.方法和变量名的大小写规则:
变量名遵循与函数名相同的约定。
d.Firstname
d.Country
使用函数命名规则:小写字母,单词之间用下划线分隔,以提高可读性。
有时,在pandas这样的库中会出现这些问题,这些库允许按名称点访问DataFrame列。解决命名限制的默认机制也是数组表示法——括号中的字符串。
如果这些约束不适用于您的用例,那么在点访问数据结构上有几个选项。
很抱歉再添加一个,但这一个解决了subdicts和纠正AttributeError,尽管非常简单:
class DotDict(dict):
def __init__(self, d: dict = {}):
super().__init__()
for key, value in d.items():
self[key] = DotDict(value) if type(value) is dict else value
def __getattr__(self, key):
if key in self:
return self[key]
raise AttributeError(key) #Set proper exception, not KeyError
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
由于以下原因,我对现有的选项不满意,于是我开发了MetaDict。它的行为完全类似于dict,但支持点表示法和IDE自动补全,而没有其他解决方案的缺点和潜在的名称空间冲突。所有功能和使用示例都可以在GitHub上找到(见上面的链接)。
完全披露:我是MetaDict的作者。
我在尝试其他解决方案时遇到的缺点/限制:
Addict
No key autocompletion in IDE
Nested key assignment cannot be turned off
Newly assigned dict objects are not converted to support attribute-style key access
Shadows inbuilt type Dict
Prodict
No key autocompletion in IDE without defining a static schema (similar to dataclass)
No recursive conversion of dict objects when embedded in list or other inbuilt iterables
AttrDict
No key autocompletion in IDE
Converts list objects to tuple behind the scenes
Munch
Inbuilt methods like items(), update(), etc. can be overwritten with obj.items = [1, 2, 3]
No recursive conversion of dict objects when embedded in list or other inbuilt iterables
EasyDict
Only strings are valid keys, but dict accepts all hashable objects as keys
Inbuilt methods like items(), update(), etc. can be overwritten with obj.items = [1, 2, 3]
Inbuilt methods don't behave as expected: obj.pop('unknown_key', None) raises an AttributeError
编辑:NeoBunch是废弃的,Munch(上面提到过)可以作为一个替代品。不过,我把这个解决方案留在这里,它可能对某些人有用。
正如Doug所指出的,有一个Bunch包,你可以使用它来实现obj。关键功能。实际上有一个更新的版本叫做
尼奥邦克·蒙克
它有一个伟大的功能,通过neobunchify函数将你的字典转换为NeoBunch对象。我经常使用Mako模板,将数据作为NeoBunch对象传递使它们更具可读性,所以如果你碰巧在你的Python程序中使用了一个普通的字典,但想要在Mako模板中使用点符号,你可以这样使用:
from mako.template import Template
from neobunch import neobunchify
mako_template = Template(filename='mako.tmpl', strict_undefined=True)
data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]}
with open('out.txt', 'w') as out_file:
out_file.write(mako_template.render(**neobunchify(data)))
Mako模板看起来像这样:
% for d in tmpl_data:
Column1 Column2
${d.key1} ${d.key2}
% endfor