首页 > 解决方案 > 基于 for 的语法跳过对“iter”的调用

问题描述

考虑以下迭代器:

class MyIterator:

  def __iter__(self):
    print("Calling `__iter__`")
    return self

  def __reversed__(self):
    print("Calling `__reversed__`")
    return self

  def __next__(self):
    print("Calling `__next__`")
    raise StopIteration

for以下代码是否存在基于 - 的等效语法:

R = reversed(MyIterator())
while True:
  try:
    next(R)
  except StopIteration:
    break

实际上打印

Calling `__reversed__`
Calling `__next__`

for语法似乎总是添加对 的调用iter,即使给定的对象已经遵守 Iterator 协议。

标签: python

解决方案


隐式调用__iter__是故意的;for不知道它是否被交给了一个 itera tor或一个 itera ble,并且需要无条件地确保它有一个 itera tor,它通过调用__iter__. 如果您的对象已经是一个迭代器,__iter__则应该是幂等的,除了返回自身之外什么都不做主体应该只是return self)。在现有的 Python 代码中有一些测试通过 testing 来检测迭代器iter(x) is x,如果你违反了规则而不是返回self自定义迭代器,你应对不当行为负责。

关键是,你想做的事情没有意义。要么写一个可迭代的(没有 a__next__和 a__iter__返回一个新的迭代器)或者写一个迭代器,但是如果你写一个迭代器,你需要遵守迭代器的规则。

为了清楚起见,这里不可避免的操作顺序是:

  1. reversedinvokes __reversed__,理论上应该返回一个新的反向迭代器(让它返回现有的,理论上的正向迭代器是非常奇怪的)
  2. for__iter__隐式调用任何reversed返回以确保它是一个迭代器(如果__reversed__实现得当,它会返回一个全新的迭代器,但是,你最终会调用__iter__同一个对象)
  3. for然后__next__为每个项目隐式调用,直到StopIteration引发

要修复它,请__reversed__返回一个新的反向迭代器,而不是self; 新的迭代器__iter__仍然会被调用,但它不会是原始迭代器上的那个。更好的是,拆分你的实现;可逆的东西首先不应该是迭代器。只需 make__iter____reversed__两个生成器函数产生它们应该产生的任何东西,并摆脱__next__,以使您的类可迭代可逆,但不是迭代


推荐阅读