检测构建独立迭代器的最便宜方法
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 since
tee` 不需要两个独立的迭代器来发挥它的魔力。
但是,如果您编写的迭代器传递了一个 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.
原始的、通过的迭代器的编写者必须以支持多个并发、独立迭代的方式编写它。
假设我正在编写一个接受可迭代对象的函数,并且我的函数想要不知道该可迭代对象是否实际上是一个迭代器。
(这种情况很常见吧?我想基本上所有的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 since
tee` 不需要两个独立的迭代器来发挥它的魔力。
但是,如果您编写的迭代器传递了一个 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.
原始的、通过的迭代器的编写者必须以支持多个并发、独立迭代的方式编写它。