生成器就是一个可迭代对象
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
生成器创建1
s=(i for i in range(5))
生成器创建2
def foo(): yield 1print(foo()) #|加括号说明执行生成器 print(foo) # |不加括号说明执行函数
生成器的使用next方法
def foo(): print(1) yield 1 #第一个next进来,发现yield后类似return返回值并退出 print(2) yield 2 #第二个next进来,发现yield后类似return返回值并退出
f=foo() print(next(f))
print(next(f))
print(next(f)) #没有yield所以报错StopIteration 改进:
def foo(): print(1) yield 1 #第一个next进来,发现yield后类似return返回值并退出 print(2) yield 2 #第二个next进来,发现yield后类似return返回值并退出
f=foo() for i in f: #说明f是可迭代对象,会自动调用next,遇到StopIteration会自动退出
print(i)
所以,我们创建了一个generator后,基本上永远不会调用next()
,而是通过for
循环来迭代它,并且不需要关心StopIteration
的错误。
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现。
可迭代对象:拥有iter方法
list
tuple
dict
string
斐波那契数列
用函数实现:
1 def fib(max):2 n, a, b = 0, 0, 13 print(a)4 while n < max:5 print(b)6 a, b = b, a + b7 n = n + 18 return 'done'
用列表生成式yield实现
def fib(max): n, a, b = 0, 0, 1 print(a) while n < max: yield b #yield保存了上次执行的地点 a, b = b, a + b n = n + 1 return 'done' f=fib(5) for i in f: print(i)
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
send赋值
1 def foo(): 2 print('a') #这儿是第一次next进来输出的 3 t1=yield 1 #遇到yield就像遇到return一样,退出返回值 4 print('b') 5 t2=yield 2 6 7 f=foo() 8 next(f) #send第一次必须有一个next让他进门 9 #f.send(None)==next(f)10 ret=f.send('aa') #这里的aa会赋值给t111 print(ret)
yield实现在单线程的情况下实现并发运算的效果
import timedef customs(n): print('%s准备吃包子了'%n) while True: baozi=yield print('%s吃了包子%s'%(n,baozi))def boss(z): a=customs('a') b=customs('b') next(a) next(b) print('%s开始做包子了!'%z) for i in range(10): time.sleep(1) print('包子做好了!') a.send(i) b.send(i)boss('xiaoyaz')