我们常用的迭代器是个对象,网上常说,它所属的类需要满足以下条件:
1. __iter__
返回本身
2. __next__
返回下一个元素,如果没有后续元素,则抛出StopIteration异常
一直不是很理解这是为啥,直到我做了一些实验,证明了上面说法是正确的。
首先明确几个概念:python中除了Iterator
以外还有个Iterable
的概念。对某个实例o
的循环则有for e in o:
和 e=next(o)
2种方法。
首先给出判断Iterator
和Iterable
的代码判断方法:
"""
本代码基于python3.7.0, 后面的代码类似,不再说明
"""
from collections.abc import Iterator, Iterable
a = ...
if isinstance(a, Iterator):
print('a is Iterator')
if isinstance(a, Iterable):
print('a is Iterable')
-
如果某个类未定义
__iter__
, 但是定义了__getitem__
, 则可以对它的实例做for
循环,循环做的是从0开始依次调用__getitem__
方法。该实例既不是Iterator
, 也不是Iterable
. -
如果某个类定义了
__iter__
, 则可以对它的实例做for
循环, 循环做的是先调用__iter__
获得返回的对象,再不断调用该返回对象的__next__
方法,直到raise StopIteration
. 该实例是Iterable
. -
如果某个类定义了
__next__
, 它的实例o
可以用next(o)
来不断迭代,直至raise StopIteration
. 该实例既不是Iterator
, 也不是Iterable
. -
如果某个类既定义了
__iter__
, 又定义了__next__
, 它的实例既是Iterator
, 也是Iterable
. -
如果某个类定义了
__iter__
, 又定义了__getitem__
, 则对该类的实例做for
循环时,__iter__
的逻辑会遮盖__getitem__
的逻辑.
因此,根据2、4两点,最合理的迭代器(Iterator
)满足了以下2个特点:
1. __iter__
返回self
2. __next__
返回下一个元素,如果没有后续元素,则抛出StopIteration异常
这就和开头讲到的定义一致了, 正如python之禅所说的:
There should be one-- and preferably only one --obvious way to do it.
尽量找一种,最好只有一种明显的解决方案。
其实,管它是Iterator
还是Iterable
, 我们关注的是对象的行为,能够顺畅的进行for
或者next
迭代,才是最重要的。
顺便补充一下: 生成器generator
也支持for
或者next
迭代。
生成器函数( generator function
)是满足以下条件的函数:
- 无须
return
-
yield
返回下一个元素 - 当函数体退出时,迭代完成
由生成器函数调用得到的就是生成器(generator
).
我的实验代码如下:
from collections.abc import Iterator, Iterable
import time
class A:
""""""
def __init__(self, name='old'):
self.name = name
self.start = 1
self.end = 10
def __next__(self):
print(f'call {self.name}.__next__')
start = self.start
self.start += 1
if start <= self.end:
return start
raise StopIteration
def __iter__(self):
print(f'call {self.name}.__iter__')
# return A('new')
return self
def __getitem__(self, item):
print(f'call __getitem__({item})')
time.sleep(1)
return 5
a = A()
for i in a:
print(i)
while True:
try:
print(next(a))
except StopIteration:
break
if isinstance(a, Iterator):
print('a is Iterator')
if isinstance(a, Iterable):
print('a is Iterable')
print('*' * 100)
i1 = a.__iter__()
i2 = i1.__iter__()
for x in i1:
print(x)
print('*' * 100)
for x in i2:
print(x)
print('*' * 100)
for x in a:
print(x)
print('+' * 100)
class B:
def __init__(self):
self.num = 0
def __getitem__(self, item: int):
if item < 10:
self.num += 1
return self.num
raise StopIteration
b = B()
for z in b:
print(z)
it = iter(b) # iter(b)保留了b的状态
if isinstance(it, Iterator):
print('it is Iterator')
if isinstance(it, Iterable):
print('it is Iterable')
for z in it:
print(z)
print('it2')
it2 = iter(b)
for z in it2:
print(z)
l = [1, 2, 3]
it = iter(l)
g = (i for i in range(10))
def consumer(x):
if x > 10:
x = yield 5
c = consumer(3)
if isinstance(l, abc.Iterable):
print('l is iterable')
if isinstance(l, abc.Iterator):
print('l is Iterator')
if isinstance(it, abc.Iterable):
print('it is iterable')
if isinstance(it, abc.Iterator):
print('it is Iterator')
if isinstance(g, abc.Iterable):
print('g is iterable')
if isinstance(g, abc.Iterator):
print('g is Iterator')
if isinstance(c, abc.Iterable):
print('c is iterable')
if isinstance(c, abc.Generator):
print('c is Generator')
if isinstance(c, abc.Iterator):
print('c is Iterator')
最后几行输出是
......
l is iterable
it is iterable
it is Iterator
g is iterable
g is Iterator
c is iterable
c is Generator
c is Iterator
网友评论