如何在Python中使用Abstract Base Classes实现dict?

Aar*_*all 9 python dictionary abc

我尝试使用抽象基类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__
Run Code Online (Sandbox Code Playgroud)

一个好的答案将演示如何使这项工作,特别是没有子类化dict(我非常熟悉的概念).

Aar*_*all 21

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

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

这是错误消息: TypeError: Can't instantiate abstract class D with abstract methods __delitem__, __getitem__, __iter__, __len__, __setitem__

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

履行

所以我实现了一个映射,它在大多数方面都像dict一样,使用对象的属性引用dict进行映射.(委托与继承不同,所以我们只是委托给实例__dict__,我们可以使用任何其他ad-hoc映射,但你似乎并不关心实现的那一部分.这样做是有意义的这种方式在Python 2中,因为MutableMapping在Python 2中没有__slots__,所以你要创建一种__dict__方式.在Python 3中,你可以通过设置完全避免dicts __slots__.)

import collections

class D(collections.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__)
Run Code Online (Sandbox Code Playgroud)

示范

并演示用法:

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

为了确保dict API,所学到的经验是,您可以随时检查collections.MutableMapping:

>>> isinstance(d, collections.MutableMapping)
True
>>> isinstance(dict(), collections.MutableMapping)
True
Run Code Online (Sandbox Code Playgroud)

虽然由于在集合导入时注册了dict总是将成为MutableMapping的实例,但反过来并不总是如此:

>>> isinstance(d, dict)
False
>>> isinstance(d, (dict, collections.MutableMapping))
True
Run Code Online (Sandbox Code Playgroud)

执行此练习后,我很清楚使用Abstract Base Classes只为该类用户提供标准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'
Run Code Online (Sandbox Code Playgroud)

人们可以掩盖内置的dict方法,如getsetdefault

>>> d['get'] = 'baz'
>>> d.get('get')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
Run Code Online (Sandbox Code Playgroud)

再次取消屏蔽非常简单:

>>> del d['get']
>>> d.get('get', 'Not there, but working')
'Not there, but working'
Run Code Online (Sandbox Code Playgroud)

但我不会在生产中使用此代码.


没有字典的演示,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__'
Run Code Online (Sandbox Code Playgroud)