2

我还没有看到字典点访问的可切换版本。

我在这里的第一次尝试不起作用:

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__
4

1 回答 1

9

如果你设置self.__dict__ = self了,那么字典会自动变成“dottable”。您可以通过设置来关闭“dot-ability” self.__dict__ = {}。但是,仍然可以通过索引访问键值对。这个想法主要来自katrielalex 的 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__中列出的所有基的所有 sd.__class__.mro()有关属性查找的完整详细信息,请参阅Shalabh Chaturvedi 的这篇优秀文章)。

不管怎样,对我们来说重要的一点是所有的键值对d.__dict__都可以用点符号来访问。

d这个事实意味着我们可以通过设置自身d.__dict__来获得对键值对的点访问dd,毕竟,是一个字典,并且d.__dict__需要一个类似字典的对象。请注意,这也是节省内存的。我们没有复制任何键值对,我们只是直接d.__dict__指向一个已经存在的字典。

此外,通过分配d.__dict__dict(),我们有效地关闭了对 中的键值对的点访问d。(这并没有完全禁用点访问——例如,键值对d.__class_.__dict__仍然可以通过点表示法访问。谢天谢地,这是真的,否则您将无法allowDotting再次调用该方法!)

现在您可能想知道这是否会删除所有键值对d本身。答案是不。

键值对不存储在__dict__属性中。实际上,普通的 dict 没有__dict__属性。因此,设置d.__dict__ = {}只是将 dict 重置为中性状态。我们本可以使用

del self.__dict__

而不是

self.__dict__ = dict()

也。但是,由于在DottableDict中给定了一个__dict__属性,对我来说,允许 的实例始终具有一个属性__init__似乎更清晰。DottableDict__dict__


在您注意到的评论中:

步骤:关闭访问,将 d.foo 设置为 'bar',打开访问,d.foo 从各处消失。

要保留在关闭时设置的属性d.fooallowDotting您需要存储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

按照惯例,以单个下划线开头的属性被理解为私有的实现细节。'_attribute'我通过在字典中引入私钥来扩展这里的约定。

于 2013-08-27T11:59:31.267 回答