检测构建独立迭代器的最便宜方法

Detecting cheapest way to build independent iterators

假设我正在编写一个接受可迭代对象的函数,并且我的函数想要不知道该可迭代对象是否实际上是一个迭代器。

(这种情况很常见吧?我想基本上所有的itertools函数都是这样写的。拿一个iterable,return一个iterator。)

例如,如果我在一个对象上调用 itertools.tee(•, 2),而它碰巧还不是迭代器,这大概意味着只调用 iter 两次会更便宜得到我的两个独立迭代器。 itertools 函数是否足够智能以了解这一点,如果不是,以这种方式避免不必要成本的最佳方法是什么?

观察:

>>> def foo(x):
...     return x.__iter__() # or return iter(x)
...
>>> l = [0, 1]
>>> it = l.__iter__()
>>> it
<list_iterator object at 0x00000190F59C3640>
>>> print(foo(l), foo(it))
<list_iterator object at 0x00000190F5980AF0> <list_iterator object at 0x00000190F59C3640>

因此您无需担心函数的参数是 iterable 还是已经是 iterator。您可以在已经是迭代器的对象上调用方法 __iter__,在这种情况下它只是 returns self。这 不是 一个昂贵的调用,并且比你可能做的任何测试它是否是一个迭代器的方法都要便宜,比如它是否有一个 __next__ 方法(和如果没有,则无论如何都必须调用 __iter__

更新

我们现在看到,自从调用 iterable 与传递迭代器(当然取决于迭代器的编写方式)相比,传递给您的函数有点不同=17=] 两次调用前者会给你两个不同的迭代器,而调用 iter 两次后者则不会。例如,itertools.tee 需要一个 iterable。如果你传递给它一个实现 __iter__ 的迭代器,那么 returns 'selfit will clearly work sincetee` 不需要两个独立的迭代器来发挥它的魔力。

但是,如果您编写的迭代器传递了一个 iterable,它是通过在传递的 iterator[=48= 上使用两个或多个迭代器在内部实现的], 你真正想要测试的是传递的东西是否支持多个、并发、独立的迭代,无论它是一个迭代器还是一个普通的 迭代器:

def my_iterator(iterable):
     it1 = iter(iterable)
     it2 = iter(iterable)
     if it1 is it2:
          raise ValueError('The passed iterable does not support multiple, concurrent, independent iterations.')
     ...

class Foo:
     def __init__(self, lst):
          self.lst = lst

     def __iter__(self):
          self.idx = 0
          return self

     def __next__(self):
          if self.idx < len(self.lst):
               value = self.lst[self.idx]
               self.idx += 1
               return value
          raise StopIteration()

f = Foo("abcd")
for x in f:
     print(x)

my_iterator(f)

打印:

a
b
c
d
Traceback (most recent call last):
  File "C:\Booboo\test\test.py", line 26, in <module>
    my_iterator(f)
  File "C:\Booboo\test\test.py", line 5, in my_iterator
    raise ValueError('The passed iterable does not support multiple, concurrent, independent iterations.')
ValueError: The passed iterable does not support multiple, concurrent, independent iterations.

原始的、通过的迭代器的编写者必须以支持多个并发、独立迭代的方式编写它。