如何用Python中的抽象基类实现dict?

如何用Python中的抽象基类实现dict?,python,dictionary,abc,Python,Dictionary,Abc,我试图使用抽象基类MutableMapping在Python中实现映射,但在实例化时出错。我该如何着手制作一个工作版本的字典,用抽象基类以尽可能多的方式模拟内置的dict类呢 >>> class D(collections.MutableMapping): ... pass ... >>> d = D() Traceback (most recent call last): File "<stdin>", line 1, in <

我试图使用抽象基类MutableMapping在Python中实现映射,但在实例化时出错。我该如何着手制作一个工作版本的字典,用抽象基类以尽可能多的方式模拟内置的
dict
类呢

>>> class D(collections.MutableMapping):
...     pass
... 
>>> d = D()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class D with abstract methods __delitem__, __getitem__, __iter__, __len__, __setitem__
类D(集合.可变映射): ... 通过 ... >>>d=d() 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 TypeError:无法使用抽象方法\uuuu delitem\uuuuu、\uuuu getitem\uuuuu、\uuuuuu iter\uuuuu、\uuuuu len\uuuuuu、\uuuuuu setitem实例化抽象类D__
一个好的答案将演示如何实现这一点,特别是在不将
dict
()子类化的情况下。

使用
可变映射作为基类时,您应该在类中自己创建此方法:
\uu delitem\uuuuuuu,\uuu getitem\uuuu,\uuuu iter\uu,\uu len uuuu,\uuuuu setitem\uuu

from collections.abc import MutableMapping

class D(MutableMapping):
    '''
    Mapping that works like both a dict and a mutable object, i.e.
    d = D(foo='bar')
    and 
    d.foo returns 'bar'
    '''
    # ``__init__`` method required to create instance from class.
    def __init__(self, *args, **kwargs):
        '''Use the object dict'''
        self.__dict__.update(*args, **kwargs)
    # The next five methods are requirements of the ABC.
    def __setitem__(self, key, value):
        self.__dict__[key] = value
    def __getitem__(self, key):
        return self.__dict__[key]
    def __delitem__(self, key):
        del self.__dict__[key]
    def __iter__(self):
        return iter(self.__dict__)
    def __len__(self):
        return len(self.__dict__)
    # The final two methods aren't required, but nice for demo purposes:
    def __str__(self):
        '''returns simple dict representation of the mapping'''
        return str(self.__dict__)
    def __repr__(self):
        '''echoes class, id, & reproducible representation in the REPL'''
        return '{}, D({})'.format(super(D, self).__repr__(), 
                                  self.__dict__)
要创建自定义dict类,可以从dict派生:

>>> class D(dict):
...     pass
... 
>>> d = D()
>>> d
{}

抽象基类的全部思想是它有一些成员,(C++ C++中的纯虚拟成员),代码必须提供给C++,这些是纯虚成员和其他虚拟成员(<强> > <强> >覆盖。

Python与C++不同,因为所有类的所有成员都是虚拟的,可以被重写,(并且可以向所有类和实例添加成员),但抽象基类有一些需要的缺失类,这些类是C++纯虚的等价物。 解决了这个问题后,您只需要提供缺少的成员就可以创建派生类的实例


例如,如果你想做的事情是看到公认的答案,而不是在课堂上使用dict,那么你必须提供dict为你自己提供的方法。

至少

您需要在子类中实现从MutableMapping继承的所有抽象方法

class D(MutableMapping):
    def __delitem__(self):
        '''
         Your Implementation for deleting the Item goes here
        '''
        raise NotImplementedError("del needs to be implemented")
    def __getitem__(self):
        '''
         Your Implementation for subscripting the Item goes here
        '''
        raise NotImplementedError("obj[index] needs to be implemented")
    def __iter__(self):
        '''
         Your Implementation for iterating the dictionary goes here
        '''
        raise NotImplementedError("Iterating the collection needs to be implemented")
    def __len__(self):
        '''
         Your Implementation for determing the size goes here
        '''
        raise NotImplementedError("len(obj) determination needs to be implemented")
    def __setitem__(self):
        '''
         Your Implementation for determing the size goes here
        '''
        raise NotImplementedError("obj[index] = item,  needs to be implemented")


>>> D()
<__main__.D object at 0x0258CD50>
D类(可变映射):
定义文件(自身):
'''
删除该项的实现如下所示
'''
引发未实施错误(“需要实施del”)
定义获取项目(自身):
'''
订阅项目的实现如下所示
'''
raise NOTEImplementedError(“需要实现obj[索引])
定义(自我):
'''
这里是迭代字典的实现
'''
raise NOTEImplementedError(“需要实现对集合的迭代”)
定义(自我):
'''
用于确定大小的实现位于此处
'''
提出未实施错误(“需要实施len(obj)确定”)
定义设置项(自身):
'''
用于确定大小的实现位于此处
'''
raise NOTEImplementedError(“对象[索引]=项,需要实现”)
>>>D()
此外

您需要提供一个数据结构来存储映射(hash、AVL、Red-Black),并提供一种构建字典的方法

如何使用抽象基类实现dict?

一个好的答案将展示如何使这项工作,特别是 没有子类化dict

下面是错误消息:
TypeError:无法用抽象方法实例化抽象类D\uuuu delitem\uuuu、\uuuu getitem\uuuuu、\uuuuu iter\uuuuu、\uuu len\uuuuu、\uuuuu setitem\uuu

事实证明,必须实现它们才能使用抽象基类(ABC),
MutableMapping

实施 因此,我实现了一个映射,它在大多数方面都像dict一样使用对象的属性引用dict进行映射。(委派与继承不同,因此我们将只委派给实例
\uuuuu dict\uuuuu
,我们可以使用任何其他特殊映射,但您似乎并不关心实现的这一部分。在Python 2中这样做是有意义的,因为在Python 2中,可变映射没有
\uuuu插槽
,因此您正在创建一个
\uuuuu dict\uuuu
任何一种方式。在Python 3中,您可以通过设置
\uuuu插槽来避免dict)

示范 并演示其用法:

>>> d = D((e, i) for i, e in enumerate('abc'))
>>> d
<__main__.D object at 0x7f75eb242e50>, D({'b': 1, 'c': 2, 'a': 0})
>>> d.a
0
>>> d.get('b')
1
>>> d.setdefault('d', []).append(3)
>>> d.foo = 'bar'
>>> print(d)
{'b': 1, 'c': 2, 'a': 0, 'foo': 'bar', 'd': [3]}
虽然由于集合导入上的注册,dict始终是可变映射的实例,但情况并非总是相反:

>>> isinstance(d, dict)
False
>>> isinstance(d, (dict, MutableMapping))
True
在完成这个练习之后,我很清楚,使用抽象基类只能为类的用户提供标准API的保证。在这种情况下,假定有MutableMapping对象的用户将得到Python标准API的保证

注意事项: 未实现
fromkeys
类构造函数方法

>>> dict.fromkeys('abc')
{'b': None, 'c': None, 'a': None}
>>> D.fromkeys('abc')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'D' has no attribute 'fromkeys'
再次揭开面具相当简单:

>>> del d['get']
>>> d.get('get', 'Not there, but working')
'Not there, but working'
但我不会在生产中使用此代码


不带dict的演示,Python 3:

>>> class MM(MutableMapping):
...   __delitem__, __getitem__, __iter__, __len__, __setitem__ = (None,) *5
...   __slots__ = ()
...
>>> MM().__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MM' object has no attribute '__dict__'
>MM类(可变映射):
...   __delitem_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
...   __插槽\uuux=()
...
>>>MM()。uu dict__
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:'MM'对象没有属性'\uu dict''

在任何地方都不实际使用
dict
来演示这一点的最好方法可能是实现一些非常简单的东西,与
dict
非常不同,并且不是完全无用。类似于固定大小
字节
到相同固定大小
字节
的固定大小映射。(例如,您可以将其用于路由表,它将比
dict
将未打包的键映射到未打包的值要紧凑得多,尽管这显然是以速度和灵活性为代价的。)

哈希表只是
(哈希、键、值)
元组的数组。由于这一点的全部目的是打包数据,我们将这些数据塞进一个
结构中,这意味着我们可以使用一个大的
字节数组

>>> del d['get']
>>> d.get('get', 'Not there, but working')
'Not there, but working'
>>> class MM(MutableMapping):
...   __delitem__, __getitem__, __iter__, __len__, __setitem__ = (None,) *5
...   __slots__ = ()
...
>>> MM().__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MM' object has no attribute '__dict__'
class FixedHashTable(object):
    hashsize = 8
    def __init__(self, elementsize, size):
        self.elementsize = elementsize
        self.size = size
        self.entrysize = self.hashsize + self.elementsize * 2
        self.format = 'q{}s{}s'.format(self.elementsize, self.elementsize)
        assert struct.calcsize(self.format) == self.entrysize
        self.zero = b'\0' * self.elementsize
        self.store = bytearray(struct.pack(self.format, 0,
                                           self.zero, self.zero)
                               ) * self.size
    def hash(self, k):
        return hash(k) or 1
    def stash(self, i, h, k, v):
        entry = struct.pack(self.format, h, k, v)
        self.store[i*self.entrysize:(i+1)*self.entrysize] = entry
    def fetch(self, i):
        entry = self.store[i*self.entrysize:(i+1)*self.entrysize]
        return struct.unpack(self.format, entry)
    def probe(self, keyhash):
        i = keyhash % self.size
        while True:
            h, k, v = self.fetch(i)
            yield i, h, k, v
            i = (i + 1) % self.size
            if i == keyhash % self.size:
                break
class FixedDict(collections.abc.MutableMapping):
    def __init__(self, elementsize, size):
        self.hashtable = FixedHashTable(elementsize, size)
        self.len = 0
    def __getitem__(self, key):
        keyhash = self.hashtable.hash(key)
        for i, h, k, v in self.hashtable.probe(keyhash):
            if h and k == key:
                return v
    def __delitem__(self, key):
        keyhash = self.hashtable.hash(key)
        for i, h, k, v in self.hashtable.probe(keyhash):
            if h and k == key:
                self.hashtable.stash(i, 0, self.hashtable.zero, self.hashtable.zero)
                self.len -= 1
                return
        raise KeyError(key)
    def __setitem__(self, key, value):
        keyhash = self.hashtable.hash(key)
        for i, h, k, v in self.hashtable.probe(keyhash):
            if not h or k == key:
                if not h:
                    self.len += 1
                self.hashtable.stash(i, keyhash, key, value)
                return
        raise ValueError('hash table full')
def __iter__(self):
    return (k for (h, k, v) in self.hashtable.fetch(i)
            for i in range(self.hashtable.size) if h)
def __repr__(self):
    return '{}({})'.format(type(self).__name__, dict(self.items()))
def items(self):
    pairs = ((k, v) for (h, k, v) in self.hashtable.fetch(i)
             for i in range(self.hashtable.size) if h)
    return collections.abc.ItemsView._from_iterable(pairs)