像dict一样运行的python类

sky*_*gle 97 python dictionary

我想写一个行为类似的自定义类dict- 所以,我继承自dict.

但我的问题是:我是否需要dict在我的__init__()方法中创建一个私有成员?我没有看到这一点,因为我已经有了dict行为,如果我只是继承dict.

任何人都可以指出为什么大多数继承片段看起来像下面的那样?

class CustomDictOne(dict):
   def __init__(self):
      self._mydict = {} 

   # other methods follow
Run Code Online (Sandbox Code Playgroud)

而不是更简单......

class CustomDictTwo(dict):
   def __init__(self):
      # initialize my other stuff here ...

   # other methods follow
Run Code Online (Sandbox Code Playgroud)

实际上,我认为我怀疑这个问题的答案是用户不能直接访问你的字典(即他们必须使用你提供的访问方法).

但是,阵列访问运算符[]呢?如何实现这一点?到目前为止,我还没有看到一个显示如何覆盖[]运算符的示例.

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

我尝试了以下代码片段来测试我对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]
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

KeyError:<内置函数id>

上面的代码有什么问题?

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

md = myDict()
md['id'] = 123
Run Code Online (Sandbox Code Playgroud)

[编辑]

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

Ric*_*son 93

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}
Run Code Online (Sandbox Code Playgroud)

  • 如果你要继承`dict`,那么你应该使用对象本身(使用`super`)而不是简单地委托给实例的`__dict__` - 这实际上意味着你要为每个实例创建两个dicts. (28认同)
  • self .__ dict__与实际词典内容*不相同。每个python对象,无论其类型如何,都有一个_dict__,其中包含所有对象属性(方法,字段等)。除非您不想编写修改自身的代码,否则您不会************* (3认同)

S.L*_*ott 74

像这样

class CustomDictOne(dict):
   def __init__(self,*arg,**kw):
      super(CustomDictOne, self).__init__(*arg, **kw)
Run Code Online (Sandbox Code Playgroud)

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

你不需要包装一个隐藏的self._dict.你的班级已经一个词典.

  • 用野人用逗号后用空格!;-) (6认同)
  • 这个。从dict继承而没有先调用其构造函数是没有意义的。 (3认同)
  • `__dict__` 实际上只在第一次访问时创建,所以只要用户不尝试使用它就可以。`__slots__` 会很好。 (3认同)
  • 请注意,您继承的“dict”实际上包含2个字典实例:第一个是继承的容器,第二个是保存类属性的字典 - 您可以通过使用 [__slots__](https://docs.python .org/3/reference/datamodel.html#slots)。 (2认同)
  • @andrewtavis 我已经发布了一个演示插槽的答案。 (2认同)

Bjö*_*lex 34

查看有关模拟容器类型的文档.在你的情况下,add应该是第一个参数self.


mad*_*gan 13

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

class DictLikeClass:
    ...
    def __getitem__(self, key):
        return getattr(self, key)

    def __setitem__(self, key, value):
        setattr(self, key, value)
    ...
Run Code Online (Sandbox Code Playgroud)

你可以这样使用:

>>> d = DictLikeClass()
>>> d["key"] = "value"
>>> print(d["key"])
Run Code Online (Sandbox Code Playgroud)


Aar*_*all 10

一个Python类,其行为类似于dict

这有什么问题吗?

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

class CustomDictOne(dict):
   def __init__(self):
      self._mydict = {} 
Run Code Online (Sandbox Code Playgroud)

大概有一个很好的理由从 dict 继承(也许你已经在传递一个字典并且你想要一种更具体的字典)并且你有一个很好的理由实例化另一个字典来委托给(因为这将在每个实例实例化两个字典) )但这听起来不正确吗?

我自己从未遇到过这个用例。我确实喜欢在使用可输入的字典的情况下输入字典的想法。但在这种情况下,我更喜欢类型化类属性的想法 - 字典的重点是你可以给它任何可哈希类型的键和任何类型的值。

那么为什么我们会看到这样的片段呢?我个人认为这是一个很容易犯的错误,而且没有得到纠正,因此随着时间的推移会一直存在。

我宁愿在这些片段中看到这样的内容,以通过继承来演示代码重用:

class AlternativeOne(dict):
    __slots__ = ()
    def __init__(self):
        super().__init__()
        # other init code here
    # new methods implemented here
Run Code Online (Sandbox Code Playgroud)

或者,为了演示重新实现听写的行为,可以这样:

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
Run Code Online (Sandbox Code Playgroud)

根据请求 - 将插槽添加到 dict 子类。

为什么要添加插槽?内置dict实例没有任意属性:

class CustomDictOne(dict):
   def __init__(self):
      self._mydict = {} 
Run Code Online (Sandbox Code Playgroud)

如果我们按照大多数人在这个答案中所做的方式创建一个子类,我们会发现我们没有得到相同的行为,因为我们将有一个__dict__属性,导致我们的字典可能占用两倍的空间:

class AlternativeOne(dict):
    __slots__ = ()
    def __init__(self):
        super().__init__()
        # other init code here
    # new methods implemented here
Run Code Online (Sandbox Code Playgroud)

由于上面没有创建错误,因此上面的类实际上并没有“像dict”那样起作用。

我们可以通过给它空槽来让它像字典一样:

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
Run Code Online (Sandbox Code Playgroud)

所以现在尝试使用任意属性将会失败:

>>> d = dict()
>>> d.foo = 'bar'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'foo'
Run Code Online (Sandbox Code Playgroud)

这个 Python 类的行为更像是一个dict.

有关如何以及为何使用插槽的更多信息,请参阅此问答:__slots__ 的用法?


hi2*_*euk 10

Python 标准库中的UserDict就是为此目的而设计的。


he1*_*1ix 9

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

模拟容器类型

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


bzd*_*111 9

这是一个替代解决方案:

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

a = AttrDict()
a.a = 1
a.b = 2
Run Code Online (Sandbox Code Playgroud)