A The KeysView in _collections_abc.py should implement support for __reversed__ to match the expectation set forth in Dictionary view objects.
B The KeysView should also properly implement its own __repr__ to match how the built-in dict object reports its keys.
Below is the current implementation of KeysView:
class KeysView(MappingView, Set):
__slots__ = ()
@classmethod
def _from_iterable(cls, it):
return set(it)
def __contains__(self, key):
return key in self._mapping
def __iter__(self):
yield from self._mapping
My suggestions are
class KeysView(MappingView, Set):
__slots__ = ()
@classmethod
def _from_iterable(cls, it):
return set(it)
def __repr__(self): # B
return f'{self.__class__.__name__}([{", ".join(repr(i) for i in self)}])'
def __contains__(self, key):
return key in self._mapping
def __iter__(self):
yield from self._mapping
def __reversed__(self): # A
yield from reversed(self._mapping)
Currently, a class inheriting UserDict will not support key reversal, as shown below
from collections import UserDict
class CustomDict(UserDict):
pass
>>> d = CustomDict()
>>> d['a'] = 'b'
>>> reversed(d.keys())
TypeError: 'KeysView' object is not reversible
This is in direct conflict with what is reported as supported in Dictionary View Objects:
reversed(dictview)
Return a reverse iterator over the keys, values or items of the dictionary. The view will be iterated in reverse order of the insertion.
Changed in version 3.8: Dictionary views are now reversible.
Also, per point B, the __repr__ for the default KeysView implementation incorrectly returns the repr of the _mapping object and not only the keys.
Using the above custom class, you will get the following for .keys()
>>> d = CustomDict({v: i for i, v in enumerate('spam')})
>>> d.keys()
KeysView({'s': 0, 'p': 1, 'a': 2, 'm': 3})
The KeysView object should only return the actual keys, like how dict works:
>>> d = {v: i for i, v in enumerate('spam')}
>>> d.keys()
dict_keys(['s', 'p', 'a', 'm'])