一个类似于 dict 的 python 类
Posted
技术标签:
【中文标题】一个类似于 dict 的 python 类【英文标题】:A python class that acts like dict 【发布时间】:2011-04-30 04:49:35 【问题描述】:我想编写一个行为类似于 dict
的自定义类 - 所以,我继承自 dict
。
不过,我的问题是:我是否需要在我的 __init__()
方法中创建一个私有 dict
成员?我不明白这一点,因为如果我只是从dict
继承,我已经有了dict
的行为。
谁能指出为什么大多数继承 sn-ps 看起来像下面的那个?
class CustomDictOne(dict):
def __init__(self):
self._mydict =
# other methods follow
而不是更简单的......
class CustomDictTwo(dict):
def __init__(self):
# initialize my other stuff here ...
# other methods follow
实际上,我想我怀疑这个问题的答案是让用户无法直接访问您的字典(即他们必须使用您提供的访问方法)。
但是,数组访问运算符[]
呢?一个人将如何实施呢?到目前为止,我还没有看到显示如何覆盖 []
运算符的示例。
那么如果自定义类中没有提供[]
访问函数,继承的基方法会在不同的字典上操作?
我尝试了以下sn-p来测试我对Python继承的理解:
class myDict(dict):
def __init__(self):
self._dict =
def add(self, id, val):
self._dict[id] = val
md = myDict()
md.add('id', 123)
print md[id]
我收到以下错误:
KeyError:
上面的代码有什么问题?
如何更正 myDict
类以便我可以编写这样的代码?
md = myDict()
md['id'] = 123
[编辑]
我已经编辑了上面的代码示例,以消除我在离开办公桌之前犯的愚蠢错误。这是一个错字(我应该从错误消息中发现它)。
【问题讨论】:
【参考方案1】:class Mapping(dict):
def __setitem__(self, key, item):
self.__dict__[key] = item
def __getitem__(self, key):
return self.__dict__[key]
def __repr__(self):
return repr(self.__dict__)
def __len__(self):
return len(self.__dict__)
def __delitem__(self, key):
del self.__dict__[key]
def clear(self):
return self.__dict__.clear()
def copy(self):
return self.__dict__.copy()
def has_key(self, k):
return k in self.__dict__
def update(self, *args, **kwargs):
return self.__dict__.update(*args, **kwargs)
def keys(self):
return self.__dict__.keys()
def values(self):
return self.__dict__.values()
def items(self):
return self.__dict__.items()
def pop(self, *args):
return self.__dict__.pop(*args)
def __cmp__(self, dict_):
return self.__cmp__(self.__dict__, dict_)
def __contains__(self, item):
return item in self.__dict__
def __iter__(self):
return iter(self.__dict__)
def __unicode__(self):
return unicode(repr(self.__dict__))
o = Mapping()
o.foo = "bar"
o['lumberjack'] = 'foo'
o.update('a': 'b', c=44)
print 'lumberjack' in o
print o
In [187]: run mapping.py
True
'a': 'b', 'lumberjack': 'foo', 'foo': 'bar', 'c': 44
【讨论】:
如果你要继承dict
,那么你应该使用对象本身(使用super
)而不是简单地委托给实例的__dict__
——这实际上意味着你正在创建每个实例有两个字典。
self.__dict__ 与实际字典内容不相同。每个 Python 对象,无论其类型如何,都有一个 _dict__
,其中包含所有对象属性(方法、字段等)。你确实不想想弄乱这个,除非你想编写修改自身的代码......【参考方案2】:
这样
class CustomDictOne(dict):
def __init__(self,*arg,**kw):
super(CustomDictOne, self).__init__(*arg, **kw)
现在您可以使用内置函数,例如 dict.get()
和 self.get()
。
您不需要包装隐藏的self._dict
。您的课程已经是一个字典。
【讨论】:
这个。如果不先调用其构造函数,则从dict
继承是没有意义的。
请注意,您继承的 dict
实际上包含 2 个 dict-instance:第一个是继承的容器,第二个是包含类属性的 dict - 您可以通过使用 slots 来避免这种情况.
__dict__
实际上是在第一次访问时才创建的,所以只要用户不尝试使用它就可以了。 __slots__
不过会很好。
在你野蛮的逗号后使用空格! ;-)
@andrewtavis 我已经发布了一个演示插槽的答案。【参考方案3】:
查看emulating container types 上的文档。在您的情况下,add
的第一个参数应该是 self
。
【讨论】:
为了完整起见,这里是@björn-pollex 提到的最新Python 2.x(截至撰写本文时为2.7.7)文档的链接:Emulating Container Types(抱歉没有使用 cmets 函数,*** 不允许我这样做。)【参考方案4】:这是一个替代解决方案:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__dict__ = self
a = AttrDict()
a.a = 1
a.b = 2
【讨论】:
这很糟糕,因为您没有定义任何自定义方法,并且还有其他答案所说的其他问题。 这正是我所需要的。谢谢!【参考方案5】:这是我最好的解决方案。这个我用过很多次了。
class DictLikeClass:
...
def __getitem__(self, key):
return getattr(self, key)
def __setitem__(self, key, value):
setattr(self, key, value)
...
你可以像这样使用:
>>> d = DictLikeClass()
>>> d["key"] = "value"
>>> print(d["key"])
【讨论】:
【参考方案6】:一个类似于
dict
的python类
这是怎么回事?
谁能指出为什么大多数继承 sn-ps 看起来像下面的那个?
class CustomDictOne(dict):
def __init__(self):
self._mydict =
大概有充分的理由从 dict 继承(也许你已经在传递一个并且你想要一种更具体的 dict)并且你有充分的理由实例化另一个 dict 来委托to (因为这将为此类的每个实例实例化 两个 dicts。)但这听起来不正确吗?
我自己从来没有遇到过这个用例。我确实喜欢在您使用可输入的 dicts 的地方输入 dicts 的想法。但在那种情况下,我更喜欢类型化类属性的想法 - 字典的全部意义在于你可以给它 any hashable 类型的键,以及 any 的值> 输入。
那么为什么我们会看到这样的 sn-ps 呢?我个人认为这是一个容易犯的错误,没有得到纠正,因此随着时间的推移而延续。
我宁愿在这些 sn-ps 中看到这个,来演示通过继承来重用代码:
class AlternativeOne(dict):
__slots__ = ()
def __init__(self):
super().__init__()
# other init code here
# new methods implemented here
或者,为了演示重新实现dicts的行为,这个:
from collections.abc import MutableMapping
class AlternativeTwo(MutableMapping):
__slots__ = '_mydict'
def __init__(self):
self._mydict =
# other init code here
# dict methods reimplemented and new methods implemented here
按要求 - 将插槽添加到 dict 子类。
为什么要添加插槽?内置的 dict
实例没有任意属性:
>>> d = dict()
>>> d.foo = 'bar'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'foo'
如果我们按照大多数人在这个答案中所做的方式创建一个子类,我们会看到我们没有得到相同的行为,因为我们将有一个 __dict__
属性,导致我们的 dicts 可能占用两倍空间:
my_dict(dict):
"""my subclass of dict"""
md = my_dict()
md.foo = 'bar'
由于上面没有产生错误,上面的类实际上并没有起作用,“就像dict
。”
我们可以通过给它空槽使它像dict一样:
class my_dict(dict):
__slots__ = ()
md = my_dict()
所以现在尝试使用任意属性将会失败:
>>> md.foo = 'bar'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'my_dict' object has no attribute 'foo'
而且这个 Python 类的行为更像是 dict
。
有关如何以及为何使用插槽的更多信息,请参阅此问答:Usage of __slots__?
【讨论】:
【参考方案7】:我真的没有在任何地方看到正确的答案
class MyClass(dict):
def __init__(self, a_property):
self[a_property] = a_property
您真正需要做的就是定义自己的__init__
- 这就是它的全部内容。
另一个例子(稍微复杂一点):
class MyClass(dict):
def __init__(self, planet):
self[planet] = planet
info = self.do_something_that_returns_a_dict()
if info:
for k, v in info.items():
self[k] = v
def do_something_that_returns_a_dict(self):
return "mercury": "venus", "mars": "jupiter"
当您想要嵌入某种逻辑时,最后一个示例很方便。
无论如何...简而言之,class GiveYourClassAName(dict)
足以让您的班级表现得像听话一样。您在self
上执行的任何 dict 操作都将与常规 dict 一样。
【讨论】:
【参考方案8】:这段代码的问题:
class myDict(dict):
def __init__(self):
self._dict =
def add(id, val):
self._dict[id] = val
md = myDict()
md.add('id', 123)
...是您的“添加”方法(...以及您希望成为类成员的任何方法)需要将明确的“自我”声明为其第一个参数,例如:
def add(self, 'id', 23):
要实现运算符重载以按键访问项目,请在 docs 中查找魔术方法 __getitem__
和 __setitem__
。
请注意,由于 Python 使用 Duck Typing,实际上可能没有理由从该语言的 dict 类派生您的自定义 dict 类——在不了解您正在尝试做什么的情况下(例如,如果您需要传递将此类的实例放入某些除非isinstance(MyDict(), dict) == True
否则会中断的代码中,您最好只实现使您的类足够像dict 并停在那里的API。
【讨论】:
【参考方案9】:Python 标准库中的UserDict 就是为此目的而设计的。
【讨论】:
【参考方案10】:永远不要从 Python 内置 dict 继承!例如update
方法不会使用__setitem__
,它们在优化方面做了很多工作。使用 UserDict。
from collections import UserDict
class MyDict(UserDict):
def __delitem__(self, key):
pass
def __setitem__(self, key, value):
pass
【讨论】:
基于什么人永远不应该从内置字典继承?来自文档 (docs.python.org/3/library/collections.html#collections.UserDict):“对此类的需求已被直接从 dict 子类化的能力部分取代;但是,此类可以更容易使用,因为底层字典可作为属性访问。”同样在同一页面上:集合模块是“自 3.3 版以来已弃用,将在 3.9 版中删除:将集合抽象基类移至 collections.abc 模块。”...没有 UserDict。 我怀疑这是受到treyhunner.com/2019/04/…的启发或至少引起了共鸣 @NumesSanguis 关于第二点:弃用整个模块并将其置于更深的命名空间级别几乎没有意义。弃用警告适用于过去位于collections
和现在位于 collections.abc
中的抽象基类 (ABC)。这适用于collections.abc
中的每个班级。 UserDict
不是 ABC。 Counter
和 defaultdict
也不是。完整的模块弃用在文档中会有非常不同的说明。以上是关于一个类似于 dict 的 python 类的主要内容,如果未能解决你的问题,请参考以下文章
python 递归地将嵌套的dicts转换为嵌套的namedtuples,为您提供类似于不可变对象文字的东西
[python]Python 字典(Dictionary) update()方法