Python 在'中访问迭代器;对于in';环

Python 在'中访问迭代器;对于in';环,python,Python,据我所知,当运行以下代码时: for i in MyObject: print(i) MyObject的\uu iter\uu函数运行,for循环使用它返回的迭代器运行循环 it = iter(MyObject) for i in it: print(i) # do something with it 是否可以在循环中间访问此迭代器对象?它是一个隐藏的局部变量,还是类似的东西 我想做以下工作: for i in MyObject: blah = forloopiter

据我所知,当运行以下代码时:

for i in MyObject:
    print(i)
MyObject的
\uu iter\uu
函数运行,for循环使用它返回的迭代器运行循环

it = iter(MyObject)
for i in it:
  print(i)
  # do something with it
是否可以在循环中间访问此迭代器对象?它是一个隐藏的局部变量,还是类似的东西

我想做以下工作:

for i in MyObject:
    blah = forloopiterator()
    modify_blah(blah)
    print(i)

我之所以要这样做,是因为我正在构建一个调试器,并且我需要在迭代器被实例化后修改它(在循环执行期间添加一个要迭代的对象)。我知道这是一种黑客行为,不应该按常规进行。直接修改MyObject.items(迭代器正在迭代的内容)不起作用,因为迭代器只计算一次。所以我需要直接修改迭代器。

要访问给定对象的迭代器,可以使用内置函数

it=iter(MyObject) >>>it.next()
不,不可能访问此迭代器(除非可能使用Python C API,但这只是猜测)。如果需要,请在循环之前将其分配给变量

it = iter(MyObject)
for i in it:
  print(i)
  # do something with it
请记住,手动推进迭代器可能引发
StopIteration
异常

for i in it:
  if check_skip_next_element(i):
    try: next(it)
    except StopIteration: break

break
的使用是可以讨论的。在这种情况下,它与
continue
具有相同的语义,但是如果您想一直运行到for块的末尾,可以使用
pass

如果您不知道python中的
pdb
调试器,请尝试一下。这是我遇到过的一个非常交互式的调试器


我确信我们可以使用pdb手动控制循环迭代。但是中途更改列表,不确定。试试看

如果您想在调试器的迭代过程中向循环中插入额外的对象,则无需修改迭代器。相反,在循环结束后,跳到循环体的第一行,然后将循环变量设置为所需的对象。下面是一个PDB示例。使用以下文件:

import pdb

def f():
    pdb.set_trace()
    for i in range(5):
        print(i)
f()
我录制了一个调试会话,该会话将
15
插入到循环中:

> /tmp/asdf.py(5)f()
-> for i in range(5):
(Pdb) n
> /tmp/asdf.py(6)f()
-> print(i)
(Pdb) n
0
> /tmp/asdf.py(5)f()
-> for i in range(5):
(Pdb) j 6
> /tmp/asdf.py(6)f()
-> print(i)
(Pdb) i = 15
(Pdb) n
15
> /tmp/asdf.py(5)f()
-> for i in range(5):
(Pdb) n
> /tmp/asdf.py(6)f()
-> print(i)
(Pdb) n
1
> /tmp/asdf.py(5)f()
-> for i in range(5):
(Pdb) c
2
3
4
(由于PDB错误,您必须跳转,然后设置循环变量。如果在设置循环变量后立即跳转,PDB将丢失对循环变量的更改。)

只要您愿意依赖Python解释器的多个未记录的内部结构(在我的例子中,是CPython 3.7),就可以完成您想要做的事情-但这对你没有任何好处


迭代器不暴露于
局部变量
,也不暴露于任何其他地方(甚至不暴露于调试器)。但是,你可以间接地通过。例如:

for ref in gc.get_referrers(seq):
    if isinstance(ref, collections.abc.Iterator):
        break
else:
    raise RuntimeError('Oops')
当然,如果在同一个列表中有两个不同的迭代器,我不知道是否有任何方法可以在它们之间做出决定,但是让我们忽略这个问题


现在,你用这个做什么?你有一个迭代器在
seq
上,现在呢?你不能用一些有用的东西来代替它,比如
itertools.chain(seq[1,2,3])
。没有用于改变列表、集合等迭代器的公共API,更不用说任意迭代器了

如果您碰巧知道它是一个列表迭代器……那么,CPython 3.x
listiterator
确实是可变的。对它们进行pickle的方法是创建一个空迭代器并调用
\uuuuu setstate\uuuu
,并引用列表和索引:

>>> print(ref.__reduce__())
(<function iter>, ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],), 7)
>>> ref.__setstate__(3) # resets the iterator to index 3 instead of 7
>>> ref.__reduce__()[1][0].append(10) # adds another value
因此:

…将打印出:

0 1 2 3 4 5 6 7 8 9 2 6 10 14 18 
…而不必完全使用迭代器


你不能用电视机做同样的事情

当你在迭代过程中对一个集合进行突变时,它会影响迭代器,就像改变列表一样,但是它所做的是不确定的。毕竟,集合具有任意顺序,这只能保证在不添加或删除的情况下保持一致。如果在中间添加或删除会发生什么?您可能会得到一个完全不同的顺序,这意味着您可能会重复已经迭代过的元素,而丢失从未见过的元素。Python暗示这在任何实现中都是非法的,CPython实际上会检查它:

s = set(range(10))
for elem in s:
  print(elem, end=' ')
  if elem % 2:
    s.add(elem * 2)
print()
这将立即引起:

RuntimeError: Set changed size during iteration
那么,如果我们使用相同的技巧在Python背后找到
set\u迭代器,并尝试更改它,会发生什么呢

s = {1, 2, 3}
for elem in s:
    print(elem)
    for ref in gc.get_referrers(seq):
        if isinstance(ref, collections.abc.Iterator):
            break
    else:
        raise RuntimeError('Oops')
    print(ref.__reduce__)
在本例中,您将看到如下内容:

2
(<function iter>, ([1, 3],))
1
(<function iter>, ([3],))
3
(<function iter>, ([],))
2
(, ([1, 3],))
1.
(, ([3],))
3.
(, ([],))
换句话说,当您pickle一个
set\u迭代器时,它会创建一个剩余元素的列表,并给出用该列表构建一个新的listiterator的指令。改变临时列表显然没有任何有用的效果


一个元组怎么样?显然,你不能仅仅改变元组本身,因为元组是不可变的。但是迭代器呢

在CPython的封面下,
tuple\u迭代器
listiterator
共享相同的结构和代码(与
迭代器
类型一样,您可以通过调用
iter
对定义
\uu len\uuuuuuuuuuuu
\uu getitem\uuuuuuuuuuuuuuuuuuuu>的“旧式序列”类型进行调用而获得该类型)。因此,您可以使用完全相同的技巧获取迭代器,并减少它


但一旦你这样做,
ref.\uuuu reduce\uuu()[1][0]是seq
将再次成为真的,换句话说,它是一个元组,与你已经拥有的元组相同,并且仍然是不可变的。

请解释你正在尝试做什么。否则,这可能会导致任何不完全正确的“MyObject的
\uu iter\uuu
函数运行”的命运。更准确地说,它是iter(MyObject)
。对象不需要定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法(通过定义
\uuuuuuuuuuuuuuuuuuuuuuuuu。对于大多数类型(包括所有内置集合),修改对象会修改迭代的对象。例如,内置的
列表
2
(<function iter>, ([1, 3],))
1
(<function iter>, ([3],))
3
(<function iter>, ([],))