为什么从原始列表中弹出会使reversed(original_list)为空?
我有以下代码:
s = [1,2,3]
t = reversed(s)
for i in t:
print(i)
# output: 3,2,1
如果我从s(原始)中弹出一个元素,那么t(反向)将被清空:
s = [1,2,3]
t = reversed(s)
s.pop()
for i in t:
print(i)
# expected output: 2, 1
# actual output (nothing):
为什么会发生这种情况?
回答
看看GitHub上的cpython代码,我们可以直观地了解为什么它不再起作用。
返回的迭代器本质上需要知道最后一个索引的位置和数组的长度。如果数组的大小发生变化,迭代器将不再工作。
测试 1:增加数组长度
这也不会产生正确的结果,但迭代器确实运行:
s = [1,2,3]
t = reversed(s)
s.append(4)
for i in t:
print(i)
# output: [3, 2, 1]
测试 2:减少,然后增加长度
s = [1,2,3]
t = reversed(s)
s.pop()
s.append(4)
for i in t:
print(i)
# output: [4, 2, 1]
它仍然有效!
所以有一个内部检查来查看最后一个索引是否仍然有效,如果是,它是一个简单的 for 循环到索引 0。
如果它不起作用,迭代器返回空。
- The check is in `line 3339`: `if (index>=0 && index < PyList_GET_SIZE(seq))` where `seq` is a pointer to the original `list` object. If the `PyList_GET_SIZE` check fails the reference is removed and `NULL` is returned.
回答
在该列表上调用reversed返回一个迭代器,它是一个特殊的对象,允许您在原始列表上以相反的顺序迭代,它不是一个新列表,只能一次性使用
>>> s= [1,2,3]
>>> t = reversed(s)
>>> t
<list_reverseiterator object at 0x00000261BE8F0C40>
>>> list(t)
[3, 2, 1]
>>> list(t)
[]
>>>
并且因为这个迭代器引用了原始列表,所以当您稍后迭代迭代器时,它的任何更改都会反映出来。
更新
特别是正如 MZ 所解释的那样,如果这种变化使得列表的状态与创建迭代器时的状态不同,如果大小减小,您将一无所获,或者如果增加则列表的不完整版本
>>> s= [1,2,3]
>>> t = reversed(s)
>>> s.insert(0,23)
>>> s
[23, 1, 2, 3]
>>> list(t)
[2, 1, 23]
>>> t = reversed(s)
>>> s.append(32)
>>> list(t)
[3, 2, 1, 23]
>>> s
[23, 1, 2, 3, 32]
>>> t = reversed(s)
>>> s.pop()
32
>>> list(t)
[]
>>>
- @Deadbeef The iterator is backed by the original list, though, so changing the state of the list affects the iterator in the sense that it is not necessarily valid after the alteration. In most languages, it's usually a Bad Idea to alter a collection while you're iterating over it using an iterator pattern.
THE END
二维码