一个像dict一样的python类


113

我想编写一个行为类似的自定义类dict-因此,我继承自dict

不过,我的问题是:我需要dict在我的__init__()方法中创建一个私有成员吗?我不明白这一点,因为dict如果我仅继承自,我就已经有行为了dict

谁能指出为什么大多数继承片段看起来像下面的片段?

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

实际上,我认为我怀疑问题的答案是,用户无法直接访问您的词典(即,他们必须使用您提供的访问方法)。

但是,数组访问运算符[]呢?一个人将如何实现呢?到目前为止,我还没有看到显示如何重写[]运算符的示例。

因此,如果[]在自定义类中未提供访问函数,则继承的基本方法将在其他字典上运行吗?

我尝试了以下代码段来测试我对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:<内置函数ID>

上面的代码有什么问题?

如何更正该类,myDict以便可以编写这样的代码?

md = myDict()
md['id'] = 123

[编辑]

我已经编辑了上面的代码示例,以摆脱在离开办公桌前犯下的愚蠢错误。这是一个错字(我应该从错误消息中发现它)。

Answers:



104
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}

37
如果要使用subclass dict,则应该使用对象本身(使用super),而不是简单地委派给实例的__dict__,这实际上意味着您要为每个实例创建两个字典。
亚伦·霍尔

8
自.__ dict__是一样的实际词典内容。每个python对象(无论其类型如何)都有一个_dict__,其中包含所有对象属性(方法,字段等)。你不会,除非你想编写代码,正在修改自己......想用这个烂摊子各地
Raik

85

像这样

class CustomDictOne(dict):
   def __init__(self,*arg,**kw):
      super(CustomDictOne, self).__init__(*arg, **kw)

现在你可以使用内置的功能,如dict.get()作为self.get()

您无需包装隐藏的self._dict。您的课程已经字典。


3
这个。没有dict先调用其构造函数就可以进行继承。
sykora

1
请注意,您的继承对象dict实际上包含2个dict-instance:第一个是继承的容器,第二个是保存类属性的dict-您可以通过使用slot避免这种情况。
ankostis

1
__dict__实际上只有当第一次访问它创建的,因此只要用户不尝试使用它,它的罚款。__slots__会很好。
亚伦·霍尔

9
野蛮的逗号后再使用空格!;-)
詹姆斯·伯克

10

为了完整起见,这里是@björn-pollex提到的有关最新Python 2.x(截至撰写本文时为2.7.7)的文档的链接:

模拟容器类型

(很抱歉,您未使用注释功能,但是stackoverflow不允许这样做。)


3

此代码段的问题:

class myDict(dict):
    def __init__(self):
        self._dict = {}

    def add(id, val):
        self._dict[id] = val


md = myDict()
md.add('id', 123)

...是您的'add'方法(...以及您想成为类成员的任何方法)需要声明一个明确的'self'作为其第一个参数,例如:

def add(self, 'id', 23):

要实现操作符重载以按键访问项目,请在文档中查找magic方法__getitem____setitem__

请注意,由于Python使用Duck Typing,因此实际上可能没有理由从语言的dict类派生自定义dict类-无需更多了解您要执行的操作(例如,如果需要传递此方法的实例)类插入某个可能会中断的代码(除非isinstance(MyDict(), dict) == True),您最好实现一个使类足够像字典的API,然后停在那里。


3

这是一个替代解决方案:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

a = AttrDict()
a.a = 1
a.b = 2

这很不好,因为您没有定义任何自定义方法,并且还有其他问题所提到的其他问题。
Shital Shah

这正是我所需要的。谢谢!
jakebrinkmann,

2

我真的在任何地方都看不到正确的答案

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),足以使您的课堂像字典一样行事。您执行的任何dict操作都self将像常规dict。


1

这是我最好的解决方案。我使用了很多次。

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"])

0

永远不要继承自Python内置字典!例如update方法woldn't use __setitem__,他们做了很多优化工作。使用UserDict。

from collections import UserDict

class MyDict(UserDict):
    def __delitem__(self, key):
        pass
    def __setitem__(self, key, value):
        pass

1
基于某人永远不应从内置字典继承的内容?从文档(docs.python.org/3/library/collections.html#collections.UserDict):“直接从dict 继承子类的能力已部分取代了对此类的需要;但是,此类更容易实现因为基础字典可以作为属性来访问。” 也在同一页面上:集合模块是“自版本3.3起已弃用,将在版本3.9中删除:将集合抽象基类移至collections.abc模块。” ...没有UserDict。
NumesSanguis

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.