0

This question has been partially answered in this thread : How to correctly implement the mapping protocol in Python? But I encounter a problem that makes me think that this answer is not enough.

The python documentation (https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes) states that the Mapping abstract base class has 3 abstract methods (__getitem__, __iter__ and __len__) that must be implemented by any concrete subclass. The others methods are mixin methods, and they should not need to be re-implemented.

But if I define a class like this one :

class MappingView(Mapping):

    def __init__(self, impl=None):
        if None is impl:
            impl = dict()
        self._impl = impl

    def __getitem__(self, key):
        return self._impl[key]

    def __len__(self):
        return len(self._impl)

    def __iter__(self):
        return iter(self._impl)

1/ Implemented abstract methods work fine :

dv = MappingView({'key1': 'val1', 'key2': 'val2'})
print(dv['key2'])  # val2
print(len(dv))  # 2
for key in dv:
    print(key)  # key1, key2

2/ Some mixin methods work fine too :

dv = MappingView({'key1': 'val1', 'key2': 'val2'})
print('key1' in dv)  # True
print('key3' in dv)  # False

3/ But some others don't work :

dv = MappingView({'key1': 'val1', 'key2': 'val2'})
print(dv.keys())  # KeysView(<utils.MappingView object at 0x7fd1aa349ac0>) instead of dict_keys(['key1', 'key2'])
print(dv.values())  # ValuesView(<utils.MappingView object at 0x7fd1aa349ac0>) instead of dict_values(['val1', 'val2'])
print(dv.items())  # ItemsView(<utils.MappingView object at 0x7fd1aa349ac0>) instead of dict_items([('key1', 'val1'), ('key2', 'val2')])

This is because the current implementation of these methods is : (https://github.com/python/cpython/blob/3.11/Lib/_collections_abc.py#L786)

    def keys(self):
        "D.keys() -> a set-like object providing a view on D's keys"
        return KeysView(self)

    def items(self):
        "D.items() -> a set-like object providing a view on D's items"
        return ItemsView(self)

    def values(self):
        "D.values() -> an object providing a view on D's values"
        return ValuesView(self)

How to subclass Mapping abstract base class without having to re-implements mixin methods ?

tvataire
  • 1
  • 1
  • `for k in dv.keys(): print(k)` , for example, works as expected. What you don't have is the kind of `__repr__` you'd like for your keys, which is a completely different problem. – Thierry Lathuille Apr 11 '23 at 17:19
  • Are you sure those three don’t work? If you try to iterate over the returned objects do you get the right results? – Samwise Apr 11 '23 at 17:19
  • @thierry-lathuille : As stated in the question, the `__iter__` method works fine because this is one of the implemented abstract method. All implemented abstract methods work fine in my example. The problem is with mixin methods which shouldn't have to be re-implemented : some of them work fine, some others don't. Nothing to do with the `__repr__` method. – tvataire Apr 11 '23 at 17:31
  • @thierry-lathuille : oh ! ok ! I understand what you means. You're right. – tvataire Apr 11 '23 at 17:41
  • Please clarify in what way you find that the methods that you say don't work, like `dv.keys()`, actually don't,. The test I did with your code shows that it works perfectly: it returns the expected view object which is perfectly usable. What you do in your code is `print` this object, which prints its `__repr__`, which as can be expected is not as personalized as the view of a `dict`'s keys. – Thierry Lathuille Apr 11 '23 at 17:41
  • @ thierry-lathuille : No needs I think. I missread you first answer : I read `for k in dv: print(k)` instead of `for k in dv.keys(): print(k)`. Indeed, it's the `__repr__` method for returns of `__keys__`, `__items__` and `__values__` methods that misled me. – tvataire Apr 11 '23 at 18:00

0 Answers0