像dict一样运行的python类

时间:2010-10-25 12:42:19

标签: python dictionary

我想编写一个行为类似于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

[编辑]

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

11 个答案:

答案 0 :(得分:87)

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}

答案 1 :(得分:68)

喜欢这个

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

现在您可以使用内置函数,例如dict.get()作为self.get()

您无需包装隐藏的self._dict。您的班级 是一个词典。

答案 2 :(得分:32)

查看emulating container types上的文档。在您的情况下,add的第一个参数应为self

答案 3 :(得分:8)

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

Emulating Container Types

(很抱歉没有使用评论功能,我只是不允许通过stackoverflow这样做。)

答案 4 :(得分: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):

要实现操作员重载以按键访问项目,请在docs中查看魔术方法__getitem____setitem__

请注意,因为Python使用Duck Typing,实际上没有理由从语言的dict类派生你的自定义dict类 - 不知道你正在尝试做什么(例如,如果你需要通过将这个类的实例放到某些代码中,除非isinstance(MyDict(), dict) == True),否则你可能会更好地实现使你的类充分类似dict并停在那里的API。

答案 5 :(得分:1)

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

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。

答案 6 :(得分:1)

行为类似于dict的python类

根据请求-将插槽添加到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__属性,导致字典占用最多可能是两倍的空间:

my_dict(dict):
    """my subclass of dict""" 

md = my_dict()
md.foo = 'bar'

由于上面没有创建任何错误,因此上面的类实际上并不起作用,就像“ 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 :(得分:1)

来自 Python 标准库的

UserDict 就是为此目的而设计的。

答案 8 :(得分:0)

永远不要继承自Python内置字典!例如update方法根本不使用__setitem__,它们在优化方面做了很多工作。使用UserDict。

from collections import UserDict

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

答案 9 :(得分:0)

这是替代解决方案:

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

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

答案 10 :(得分:0)

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

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