Python学习日志——深入迭代(生成器)
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
1 list1 = range(1,99) 2 iter_list = iter(list1) 3 print(iter_list) 4 for i in range(1,10): 5 print(next(iter_list))
输出:
<range_iterator object at 0x0050B578> 1 2 3 4 5 6 7 8 9
可见iter_list 是一个iterator对象,并成功用next()获得对象下一个值。
再看如下代码: 1 next(range(1,99))
输出报错:
TypeError: 'range' object is not an iterator
可见非iterator对象是无法使用next获得值。
这里要开始说生成器generator
对于有大量元素的可迭代对象,并且是有规律对象,可不必一开始就初始化所有元素,特别是对于不知道需要多少的元素原素的可迭代对象时,这样每次计算生成新元素就可以节省大量的空间。
那么生成器怎么用呢,首先generator是一个可迭代对象,所以本身可以通过for迭代。
除此之外,再看看生成器的概念,是每次获取时候进行一次计算,计算得值进行返回,所以我们一开始是不知道生成器后面元素的,但是可以一次一次的获取,这么听起来是不是有点像迭代器。生成器,其实也是迭代器。
第一种生成器获得方法。
可以列表生成式获得,使用()包裹住生成式便得到一个generator。现在做一个简单生成器。
1 generator1 = (x for x in range(1, 99)) 2 print(generator1) 3 for i in range(1,9): 4 print(next(generator1))
输出:
<generator object <genexpr> at 0x0045E090> 1 2 3 4 5 6 7 8
可见generator就是iterator。
之前有说过列表生成式应当是:function(x) for x in range(firstIndex,endIndex),要是生成规则较为复杂,即可写匿名函数或者另外定义函数,根据每次传进来的参数,获得新的迭代值,并且生成式是可以写成多重值迭代生成形势,所以用这种方式做的生成器适用范围非常广,这里就不详细举例。
第二种生成器创建方式涉及协程的运用。
以下是一个斐波那契数列生成器
1 def fib(): 2 n, a, b = 0, 0, 1 3 while True: 4 yield b 5 a, b = b, a + b 6 n = n + 1 7 8 gen_fib = fib() 9 print(gen_fib) 10 11 for i in range(1, 9): 12 print(next(gen_fib))
输出:
<generator object fib at 0x0068E090> 1 1 2 3 5 8 13 21
这里涉及协程的概念,当第一次使用next时,生成器会从头执行,遇上yield b时,将b值返回,并且这个生成器对象会停止运行并挂起,再次调用时,会从挂起的位置继续执行知道函数停止或者遇上写一个yield进行挂起。当遇上返回值时会报错:StopIteration
这两种生成器的差异,使用列表生成式时,要是每次有自定义函数,相当于获得的值每次都是从函数中获得,迭代只是决定调用函数的参数。而上面例子中使用协程生成的生成器,是在一个生成器中对数据进行迭代,只有在创建时候可以决定参数,每一次获得值时候都是在生成器本身内部进行反复迭代。
如此看来,似乎使用列表生成式生成的生成器适用范围更广,但是要是对于对于多值迭代的生成式,维护难度却会提高很多,没有写成函数形式的生成器清晰易维护。
其实对于协程迭代形式的生成器,也可以每次获得下一个值时候传递参数过去进行计算
1 def gen(): 2 a = 1 3 while True: 4 b = yield a 5 a = b + 1 6 7 g = gen() 8 print(next(g)) 9 10 for i in range(1, 9): 11 print(g.send(i))
输出:
1 2 3 4 5 6 7 8 9
上面的代码中每次调用send(i) 就可以把i的赋值给b,并得到下次迭代结果。
重点:这种形式的生成器,当第一次调用时候由于并没又yield挂起,并且没又接收值,所以用send传参会报错,可以向上诉方法中先使用next()或者直接使用无参数的send()
posted on 2018-09-06 09:48 SaltFishYe 阅读(192) 评论(0) 编辑 收藏 举报