On Thu, Jul 21, 2016 at 5:13 AM, Steven D'Aprano <st...@pearwood.info> wrote: > On Thu, 21 Jul 2016 03:46 am, Chris Angelico wrote: > >> On Thu, Jul 21, 2016 at 3:42 AM, Ian Kelly <ian.g.ke...@gmail.com> wrote: >>> I had occasion to write something like this: >>> >>> for i, n in reversed(enumerate(x)): pass >>> >>> Of course this fails with "TypeError: argument to reversed() must be a >>> sequence". I ended up using this instead: >>> >>> for i, n in zip(reversed(range(len(x))), reversed(x)): pass >> >> At the cost of coalescing the enumeration, you could: >> >> for i, n in reversed(list(enumerate(x))): pass >> >> It's reasonably clean but less efficient. > > > Less efficient than what?
Than something that let you "enumerate the reverse" of something. Consider: # Here's what we want to happen. x = ["foo", "bar", "spam"] for i, n in reversed(enumerate(x)): # ENABLE MINDREADING MAGIC print(i, n) 2 spam 1 bar 0 foo So we could do this by first reversing the sequence, and then *enumerating down*. But enumerate() doesn't allow that - it has a 'start' parameter, but no 'step'. Let's pretend it did. for i, n in enumerate(reversed(x), len(x)-1, -1): # same result The very cleanest, IMO, would be something like this: def enumerate_reversed(seq): n = len(seq) for obj in reversed(seq): n -= 1 yield n, obj > reversed() only operates on sequences, so it can't operate on arbitrary > iterators of unknown length? Possibly of indefinite length? Of course not; but it doesn't necessarily require that the sequence exist as a list, much less as two. My simple and naive example will take any sequence, pair values with their indices, coalesce the result as a list, and then iterate backward through that list. Imagine if the sequence in question were range(1<<256), for instance. You can't list(enumerate(x)) that, but you can certainly reversed() it and get its length... well, okay, apparently len(range(1<<256)) is illegal in CPython, but len(range(1<<63-1)) is fine. You shouldn't need to list() that. So yes, my naive version is less memory efficient for large lists. > Personally, I think your version is the most straightforward and obvious > solution that works on anything. (Well, perhaps not on infinite iterators.) > Yes, you have to convert x into a list, but that's in general the only way > to use reversed() anyway. If your needs are not too great, or simplicity of > code is more important than I agree, which is why I suggested it. > The "best" solution might be some more work: you might convert x into a list > only if it's an iterator, then iterate over it in reverse: > > > def sequencefy(x): > if x is iter(x): > return list(x) > return x > > def enumerate_down(it): > seq = sequencefy(it) > n = len(seq) - 1 > for item in reversed(seq): > yield (n, item) > n -= 1 > > for i, item = enumerate_down(x): > ... > > > > An advantage of this is that it works well with lazy sequences like > (x)range. There's no need to build up a huge list of (index, item) pairs > before reversing it. Yeah, or the simpler version that I used. All depends how much complexity you really need. ChrisA -- https://mail.python.org/mailman/listinfo/python-list