>>> from collections.abc import Iterator >>> isinstance('abc', Iterator) False >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance((), Iterator) False
惊不惊喜,意不意外,字符串、列表、字典、元组都不是迭代器。那为什么它们可以在for循环中遍历呢?而且,我想,看到这里,就算你已经可以在形式上区分可迭代对象和迭代器,但是你可能会问,这有什么卵用吗?确实,没多少卵用,因为我们还不知道__iter__()、__next__()到底是个什么鬼东西。
接下来,我们通过继续探究for循环的本质来解答这些问题。
2.3 for循环的本质说到__iter__()和__next__()方法,就很有必要介绍一下iter()和next()方法了。
(1)iter()与__iter__()
__iter__()的作用是返回一个迭代器,虽然上面说过,只要实现了__iter__()方法就是可迭代对象,但是,没有实现功能(返回迭代器)总归是有问题的,就像一个村长,当选之后,那就是村长了,但是如果尸位素餐不做事,那总是有问题的。
__iter__()方法毕竟是一个特殊方法,不适合直接调用,所以Python提供了iter()方法。iter()是Python提供的一个内置方法,可以不用导入,直接调用即可。
from collections.abc import Iterator class A(): def __iter__(self): print('A类的__iter__()方法被调用') return B() class B(): def __iter__(self): print('B类的__iter__()方法被调用') return self def __next__(self): pass a = A() print('对A类对象调用iter()方法前,a是迭代器吗:', isinstance(a, Iterator)) a1 = iter(a) print('对A类对象调用iter()方法后,a1是迭代器吗:', isinstance(a1, Iterator)) b = B() print('对B类对象调用iter()方法前,b是迭代器吗:', isinstance(b, Iterator)) b1 = iter(b) print('对B类对象调用iter()方法后,b1是迭代器吗:', isinstance(b1, Iterator))
运行结果如下:
对A类对象调用iter()方法前,a是迭代器吗: False
A类的__iter__()方法被调用
对A类对象调用iter()方法后,a1是迭代器吗: True
对B类对象调用iter()方法前,b是迭代器吗: True
B类的__iter__()方法被调用
对B类对象调用iter()方法后,b1是迭代器吗: True
对于B类,因为B类本身就是迭代器,所以可以直接返回B类的实例,也就是说self,当然,你要是返回其他迭代器也没毛病。对于类A,它只是一个可迭代对象,__iter__()方法需要返回一个迭代器,所以返回了B类的实例,如果返回的不是一个迭代器,调用iter()方法时就会报以下错误:
TypeError: iter() returned non-iterator of type 'A'
(2)next()与__next__()
__next__()的作用是返回遍历过程中的下一个元素,如果没有下一个元素则主动抛出StopIteration异常。而next()就是Python提供的一个用于调用__next__()方法的内置方法。
下面,我们通过next()方法来遍历一个list:
>>> list_1 = [1, 2, 3] >>> next(list_1) Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> next(list_1) TypeError: 'list' object is not an iterator >>> list_2 = iter(list_1) >>> next(list_2) 1 >>> next(list_2) 2 >>> next(list_2) 3 >>> next(list_2) Traceback (most recent call last): File "<pyshell#24>", line 1, in <module> next(list_2) StopIteration
因为列表只是可迭代对象,不是迭代器,所以对list_1直接调用next()方法会产生异常。对list_1调用iter()后就可以获得是迭代器的list_2,对list_2每一次调用next()方法都会取出一个元素,当没有下一个元素时继续调用next()就抛出了StopIteration异常。