This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author abarnert
Recipients abarnert
Date 2015-12-15.03:38:36
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1450150718.99.0.697293755498.issue25864@psf.upfronthosting.co.za>
In-reply-to
Content
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))))`.
History
Date User Action Args
2015-12-15 03:38:39abarnertsetrecipients: + abarnert
2015-12-15 03:38:38abarnertsetmessageid: <1450150718.99.0.697293755498.issue25864@psf.upfronthosting.co.za>
2015-12-15 03:38:38abarnertlinkissue25864 messages
2015-12-15 03:38:36abarnertcreate