Q：Python的反向迭代器与发电机的行为

Why does removing items a list break reversed objects? It doesn't break gen-exprs, and appending to or modifying the list doesn't break the reversed object, and it obviously points to the original object, so why can't it give a truncated version? Perhaps some examples can clarify:

``````l = [1, 2, 3, 4]
r = reversed(l)
g = (i for i in l)
l.pop()  # returns 4
l   # returns [1, 2, 3]

for i in g:print(i)  # prints 1 2 3 (on separate lines)
for i in r:print(i)  # prints ...nothing

r = reverse(l)
g = (i for i in l)
l[1] = 4
for i in g:print(i)  # prints 1 4 3 (on separate lines)
for i in r:print(i)  # prints 3 4 1 (on separate lines)

r = reversed(l)
g = (i for i in l)
l.append(5)
l  # returns [1, 4, 3, 5] just to keep you on your toes

for i in g:print(i)  # prints 1 4 3 5 (on separate lines)
for i in r:print(i)  # prints 3 4 1   (on separate lines)
``````

So - if the genexpr is smart enough to point to the object, and just respond to however the object has changed, why doesn't reversed? It obviously doesn't make a copy, otherwise it wouldn't "fail" on the first situation, and it wouldn't pick up the 4 in the second. So it must point to the object. Why can't it just start from index -1 and work backwards?

``````l = [1, 2, 3, 4]
r = reversed(l)
g = (i for i in l)
l.pop()  # returns 4
l   # returns [1, 2, 3]

for i in g:print(i)  # prints 1 2 3 (on separate lines)
for i in r:print(i)  # prints ...nothing

r = reverse(l)
g = (i for i in l)
l[1] = 4
for i in g:print(i)  # prints 1 4 3 (on separate lines)
for i in r:print(i)  # prints 3 4 1 (on separate lines)

r = reversed(l)
g = (i for i in l)
l.append(5)
l  # returns [1, 4, 3, 5] just to keep you on your toes

for i in g:print(i)  # prints 1 4 3 5 (on separate lines)
for i in r:print(i)  # prints 3 4 1   (on separate lines)
``````

When you call reversed() on a list object, a dedicated reverse list iterator object is created; this object 'knows' how to efficiently iterate over the list in reverse order once.

To do this, when the object is created the last index in the list is stored. For your list l, that is 3 (the 4th element, counting from 0). When you then iterate, the element at that index is yielded, and the index is decremented, until an IndexError is raised *.

A Python implementation of the object would look like this:

``````class reversed_list_iterator(object):
def __init__(self, lst):
self.index = len(lst) - 1
self.lst = lst

def __iter__(self):
return self

def __next__(self):
try:
result = self.lst[self.index]
except IndexError:
self.lst = []  # switch to permanently stopped state
raise StopIteration
self.index -= 1
return result
``````

Now, when you then remove that element, the iterator exits as there is no l[3], an IndexError is raised there and then and iteration ends.

In your second example, when creating the reversed iterator, the last index is 2. You then add to the list, but iteration starts at l[2], which still exists.

The reversed list iterator cannot use relative indices, because as you discovered, the iterator is relatively tolerant of elements being added to the list. A relative index would then repeat values.

* The actual C implementation tests for the boundaries 0 <= index < len(self.lst) rather than catch IndexError, but the principle is the same.

``````class reversed_list_iterator(object):
def __init__(self, lst):
self.index = len(lst) - 1
self.lst = lst

def __iter__(self):
return self

def __next__(self):
try:
result = self.lst[self.index]
except IndexError:
self.lst = []  # switch to permanently stopped state
raise StopIteration
self.index -= 1
return result
``````

*实际的C实现测试的界限0 & lt；=指数<；len（自我。LST）而不是抓误差系数，但原理是相同的。

python  python-3.x  iterator  generator  reverse