迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退
如何判断一个对象是否可以迭代
可以使用 isinstance() 判断一个对象是否是 Iterable 对象:
In [50]: from collections import Iterable In [51]: isinstance([], Iterable) Out[51]: True
iter()函数与next()函数
list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的
__iter__
方法。>>> li = [11, 22, 33, 44, 55] >>> li_iter = iter(li) >>> next(li_iter) 11 >>> next(li_iter) 22 >>> next(li_iter) 33 >>> next(li_iter) 44 >>> next(li_iter) 55 >>> next(li_iter) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
for...in...循环的本质
for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
fib = FibIterator(10) for num in fib: print(num, end=" ")
并不是只有for循环能接收可迭代对象
除了for循环能接收可迭代对象,list、tuple等也能接收。
li = list(FibIterator(15)) print(li) tp = tuple(FibIterator(6)) print(tp)
输出:
0 11
1 11
1 11并不是只有for循环能接收可迭代对象!!!!!!!!!!!!!!!!!!!!!!!
除了for循环能接收可迭代对象,list、tuple等也能接收。!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
class FibIterator(object): """斐波那契数列迭代器""" def __init__(self, n): """ :param n: int, 指明生成数列的前n个数 """ self.n = n # current用来保存当前生成到数列中的第几个数了 self.current = 0 # num1用来保存前前一个数,初始值为数列中的第一个数0 self.num1 = 0 # num2用来保存前一个数,初始值为数列中的第二个数1 self.num2 = 1 def __next__(self): """被next()函数调用来获取下一个数""" if self.current < self.n: num = self.num1 self.num1, self.num2 = self.num2, self.num1+self.num2 self.current += 1 return num else: raise StopIteration def __iter__(self): """迭代器的__iter__返回自身即可""" return self if __name__ == '__main__': fib = FibIterator(3) for num in fib: print(num, end=" 11") print('') li = list(FibIterator(15)) print(li) tp = tuple(FibIterator(6)) print(tp)
输出:
0 11
1 11
1 11
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
(0, 1, 1, 2, 3, 5)
生成器
1. 生成器
利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。
创建生成器方法1
要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
In [15]: L = [ x*2 for x in range(5)]
In [16]: L
Out[16]: [0, 2, 4, 6, 8]
In [17]: G = ( x*2 for x in range(5))
In [18]: G
Out[18]: <generator object <genexpr> at 0x7f626c132db0>
In [19]:
L 是一个列表,而 G 是一个生成器。我们可以直接打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。
In [19]: next(G)
Out[19]: 0
In [20]: next(G)
Out[20]: 2
创建生成器方法2
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。
In [30]: def fib(n):
....: current = 0
....: num1, num2 = 0, 1
....: while current < n:
....: num = num1
....: num1, num2 = num2, num1+num2
....: current += 1
....: yield num
....: return 'done'
但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字的 就称为 生成器
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
In [39]: g = fib(5)
In [40]: while True:
....: try:
....: x = next(g)
....: print("value:%d"%x)
....: except StopIteration as e:
....: print("生成器返回值:%s"%e.value)
....: break
....:
value:1
value:1
value:2
value:3
value:5
生成器返回值:done
In [41]:
使用send唤醒
我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。
c.next()等价c.send(None)
In [43]: f = gen()
In [44]: next(f)
Out[44]: 0
In [45]: f.send('haha')
haha
Out[45]: 1