我还没有看过字典点访问的可切换版本。
我的首次尝试不起作用:
class DottableDict (dict):
def allowDotting (self, state=True):
if state:
self.__setattr__ = dict.__setitem__
self.__getattr__ = dict.__getitem__
else:
del self.__setattr__
del self.__getattr__
>>> import dot
>>> d = dot.DottableDict()
>>> d.allowDotting()
>>> d.foo = 'bar'
>>> d
{}
>>> d.foo
'bar'
>>> d.__dict__
{'__setattr__': <slot wrapper '__setitem__' of 'dict' objects>, 'foo': 'bar',
'__getattr__': <method '__getitem__' of 'dict' objects>}
>>> d.allowDotting(False)
>>> d.__dict__
{'foo': 'bar'}
我认为签名在setattr和setitem之间不匹配。
我的第二次传球似乎也应该有效,但以同样的方式失败:
class DottableDict (dict):
def dotGet (self, attr):
return dict.__getitem__(self, attr)
def dotSet (self, attr, value):
return dict.__setitem__(self, attr, value)
def allowDotting (self, state=True):
if state:
self.__getattr__ = self.dotGet
self.__setattr__ = self.dotSet
else:
del self.__setattr__
del self.__getattr__
答案 0 :(得分:7)
如果设置self.__dict__ = self
,则dict将自动变为“dottable”。您可以通过设置self.__dict__ = {}
来关闭“点能力”。但是,键值对仍然可以通过索引来访问。这个想法主要来自katrielalex's bio page:
class DottableDict(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.__dict__ = self
def allowDotting(self, state=True):
if state:
self.__dict__ = self
else:
self.__dict__ = dict()
d = DottableDict()
d.allowDotting()
d.foo = 'bar'
print(d['foo'])
# bar
print(d.foo)
# bar
d.allowDotting(state=False)
print(d['foo'])
# bar
print(d.foo)
# AttributeError: 'DottableDict' object has no attribute 'foo'
记住Python的禅,但是:
应该有一个 - ,最好只有一个 - 显而易见的方法。
通过限制自己使用dict访问的标准语法,可以提高自己和他人的可读性/可维护性。
工作原理:
当您键入d.foo
时,Python会在多个位置查找'foo'
,其中一个位于d.__dict__
。 (它还会查看d.__class__.__dict__
以及__dict__
中列出的所有基础的所有d.__class__.mro()
...有关属性查找的完整详细信息,请参阅此excellent article by Shalabh Chaturvedi)
无论如何,对我们来说重点是d.__dict__
中的所有键值对都可以用点表示法访问。
这一事实意味着我们可以通过将d
设置为d.__dict__
来获得d
中键值对的点访问权限!毕竟,d
是一个字典,而d.__dict__
期望一个类似字母的对象。请注意,这也是内存效率高的。我们不会复制任何键值对,我们只是简单地将d.__dict__
指向已经存在的词典。
此外,通过将d.__dict__
分配给dict()
,我们有效地关闭了对d
中键值对的点访问。 (这并不能完全禁用点访问 - 例如d.__class_.__dict__
中的键值对,仍然可以通过点表示法访问。谢天谢地,这是真的,或者你无法调用{{1}再次方法!)
现在您可能想知道这是否会删除allowDotting
本身中的所有键值对。答案是否定的。
键值对未存储在d
属性中。实际上,普通的dict没有__dict__
属性。因此设置__dict__
只会将字典重置为中性条件。我们本来可以用
d.__dict__ = {}
insteaad
del self.__dict__
太。但是,由于self.__dict__ = dict()
在DottableDict
中被赋予了__dict__
属性,因此允许__init__
的实例始终具有DottableDict
属性似乎更加清晰。
您在评论中注意到:
步骤:关闭访问权限,将d.foo设置为“bar”,打开访问权限,d.foo为 从各地走了。
要保留在__dict__
关闭时设置的d.foo
等属性,您需要存储已设置allowDotting
的备用字典。
self.__dict__
按照惯例,以单个下划线开头的属性被理解为私有的实现细节。我通过在dict中引入私钥class DottableDict(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self['_attributes'] = dict()
self.allowDotting()
def allowDotting(self, state=True):
if state:
self.update(self['_attributes'])
self.__dict__ = self
else:
self.__dict__ = self['_attributes']
d = DottableDict()
d.allowDotting(state=False)
d.foo = 'bar'
d.allowDotting(state=True)
print(d.foo)
# bar
d.allowDotting(state=False)
print(d.foo)
# bar
d.allowDotting(state=True)
print(d.foo)
# bar
来扩展约定。