Gar*_*ler 2 python dictionary setattr getattr
我还没有看过字典点访问的可切换版本.
我在这里的首次尝试不起作用:
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'}
Run Code Online (Sandbox Code Playgroud)
我认为签名在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__
Run Code Online (Sandbox Code Playgroud)
如果你设置self.__dict__ = self,那么dict将自动变为"dottable".您可以通过设置关闭"点能力" self.__dict__ = {}.但是,键值对仍然可以通过索引来访问.这个想法主要来自katrielalex的生物页面:
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'
Run Code Online (Sandbox Code Playgroud)
但请记住Python的Zen:
应该有一个 - 最好只有一个 -明显的方法来做到这一点.
通过将自己限制为dict访问的标准语法,可以提高自己和他人的可读性/可维护性.
这个怎么运作:
当你输入时d.foo,Python 'foo'在许多地方寻找,其中一个在d.__dict__.(它也会查看d.__class__.__dict__,以及__dict__列出的所有基础的所有内容d.__class__.mro()......有关属性查找的完整详细信息,请参阅Shalabh Chaturvedi撰写的这篇优秀文章).
无论如何,对我们来说重点是所有的键值对d.__dict__都可以用点符号来访问.
这一事实意味着我们可以得到点访问键值对d设置d.__dict__到d自己!d毕竟,是一个字典,并d.__dict__期望一个类似字母的对象.请注意,这也是内存效率高的.我们不会复制任何键值对,我们只是简单地指向d.__dict__已存在的词典.
此外,通过分配d.__dict__至dict(),我们有效点接入关闭在键值对d.(这并不完全禁用点访问 - d.__class_.__dict__例如,键值对仍然可以通过点表示法访问.谢天谢地,这是真的,否则你将无法allowDotting再次调用该方法!)
现在您可能想知道这是否会删除所有键值对d.答案是不.
键值对不存储在__dict__属性中.实际上,普通的dict没有__dict__属性.所以设置d.__dict__ = {}只是将字典重置为中性条件.我们本来可以用的
del self.__dict__
Run Code Online (Sandbox Code Playgroud)
insteaad
self.__dict__ = dict()
Run Code Online (Sandbox Code Playgroud)
太.但是,由于DottableDict赋予了__dict__属性__init__,因此允许实例DottableDict始终具有__dict__属性似乎更清晰.
在评论中你注意到:
步骤:关闭访问权限,将d.foo设置为"bar",打开访问权限,d.foo从各处消失.
要保留已关闭时d.foo设置的属性allowDotting,您需要存储self.__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
Run Code Online (Sandbox Code Playgroud)
通过传统,以单个下划线开头的属性被理解为私有的实现细节.我通过'_attribute'在dict中引入私钥来扩展约定.