本文从以下三个方面介绍:
生成器概念
生成器用法
使用生成器的好处(作用)
一、生成器概念:
Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。主要有两种结构可以延迟结果创建。生成器函数和生成器表达式。
生成器函数:和其他函数编写方式相同,但是使用yield语句一次返回一个结果,在每个结果之间挂起当前状态,下次调用的时候会从当前状态继续。例如:
我们发现它在for循环中输出的值是接上一条语句调用之后的。
生成器表达式:类似于列表解析,不同的是它返回的是一个可迭代对象而不是一个列表。
例如:
注意:生成器是一个对象,我们用的是()而不是【】。用【】就等于创建一个列表。
一句话总结,任何使用了yield的函数就是生成器,生成器就是一个返回迭代器的函数,或者说生成器就是一个迭代器。
二、总结yield
1.和return相似,返回值。
2.暂停迭代,直到调用下一个next()方法。
3.调用的时候返回生成器对象。
三、生成器的用法:
1、close()方法
close()方法就是关闭生成器。生成器被关闭后,再次调用next()方法,不管能否遇到yield关键字,都会立即抛出StopIteration异常。
2、send()方法
这是我认为生成器最重要的功能,我们可以通过send()方法,向生成器内部传递参数。我们来看个例子:
我们第一次传进去的值将value赋值为5,第一次调用x输出值为0,然后我们将参数传进去,改变了value的值,再次输出就是我们传入的值了。简单的说,send()就是next()的功能,加上传值给yield。
3、throw()方法
除了向生成器函数内部传递参数,我们还可以传递异常。还是先看例子:
我们传入了valueError的异常,成功触发了except中的异常。
4、用yield实现斐波那契数列:
四、生成器的好处(作用)?
我们发现生成器和迭代器差不多,但是用生成器有什么好处?
1、节省内存,特别是大量数据的时候。比如我们生成一个列表:mylist = [1, 2, 3],也可以是mylist = [x**2 for x in range(3)],但是这样程序运行的时候,列表的数据全部存储在内存中,当数据量特别大的时候,会特别消耗内存。但是,利用生成器我们可以这样gen = (x ** 2 for x in range(4)),生成器只有在用的时候会生成,所以,当我们没有用到这个列表的时候它就不会存在,也不会占内存。
如果有人不信可以试试这这两段代码:
分开运行哦,记得看看内存和cpu占用(里面0尽量多一点)
2、 节省代码,减少代码量同时提高代码可读性。请看下面两个函数,他们实现的功能是相同的,但是利用生成器代码量就会减少。
3、 模拟并发。Python虽然支持多线程,但是由于GIL(全局解释锁,Global Interpreter Lock)的存在,同一个时间,只能有一个线程在运行,所以无法实现真正的并发。这时就出现了协程。复杂解释不说了,简单说协程就是你可以暂停执行的函数"。也就是yield。
Python实现协程最简单的方法,就是使用yield。当一个函数在执行过程中被阻塞时,就用yield挂起,然后执行另一个函数。当阻塞结束后,可以用next()或者send()唤醒。相比多线程,协程的好处是它在一个线程内执行,避免线程之间切换带来的额外开销,而且协程不存在加锁的步骤。
下面是一个生产者与消费者例子:
执行结果
通过执行结果,我们会看到控制台交替打印出生产和消费的结果。消费者consumer()函数是一个生成器函数,每次执行到yield时即挂起,并返回上一次的结果给生产者。生产者producer()接收到生成器的返回,并生成一个新的值,通过send()方法发送给消费者。至此,我们成功实现了一个(伪)并发。
码字不易,多多关注,谢谢。不足之处欢迎指正。
领取专属 10元无门槛券
私享最新 技术干货