New submission from Andrew Barnert:
Example:
class MyDict(collections.abc.Mapping):
def __init__(self, d): self.d = d
def __len__(self): return len(self.d)
def __getitem__(self, key): return self.d[key]
def __iter__(self): return iter(self.d)
d = {1:2, 3:4}
m = MyDict({1: 2, 3: 4})
If you do `reversed(d)`, you get a nice `TypeError: argument to reversed() must
be a sequence`. But if you do `reversed(m)`, you get a `reversed` iterator. And
when you iterate it, presumably expecting to get 0 and 1 in some arbitrary
order, you instead get 3, and then a `KeyError: 0`.
Of course it's obvious why this happens once you think about it: in order to
handle types that implement the old-style sequence protocol (just respond to
`__getitem__` for all integers from 0 to `len(self)`), `reversed` has to fall
back to trying `__getitem__` for all integers from `len(d)-1` to 0.
If you provide a `__reversed__` method, it just calls that. Or, if you're a
C-API mapping like `dict`, `PySequence_Check` will return false and it'll raise
a `TypeError`. But for a Python mapping, there's no way `PySequence_Check` or
anything else can know that you're not actually a sequence (after all, you
implement `__getitem__` and `__len__`), so it tries to use you as one, and
confusion results.
I think trying to fix this for _all_ possible mappings is a non-starter.
But fixing it for mappings that use `collections.abc.Mapping` is easy: just
provide a default implementation of `collections.abc.Mapping.__reversed__` that
just raises a `TypeError`.
I can't imagine this would break any working code. If it did, the workaround
would be simple: just implement `def __reversed__(self): return (self[k] for k
in reversed(range(len(self))))`.
----------
components: Library (Lib)
messages: 256434
nosy: abarnert
priority: normal
severity: normal
status: open
title: collections.abc.Mapping should include a __reversed__ that raises
TypeError
type: behavior
_______________________________________
Python tracker <[email protected]>
<http://bugs.python.org/issue25864>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com