概述
Python 生成器:边循环边计算
文章目录
- Python 生成器:边循环边计算
- 一、生成器的运行机制
- 二、创建生成器
- 三、生成器的第一 次调用
- 四、使用协程重置生成器序列
在Python程序中,使用关键字yield定义的函数被称为生成器( Generator )。通过使用生成器,可以生成一个值的序列用于迭代,并且这个值的序列不是一次生成的,而是使用一个,再生成一个,最大的好处是可以使程序节约大量内存。
一、生成器的运行机制
在Python程序中,生成器是一个记住上一次返回时在函数体中位置的函数。对生成器函数的第二次(或第n次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。生成器不仅“记住”了它的数据状态,还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。概括来说,生成器的特点如下所示。
(1)生成器是一个函数,而且函数的参数都会保留。
(2)当迭代到下一次的调用时,所使用的参数都是第一次所保留 下的。也就是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的。
在Python程序中,使用关键字yield定义生成器。当向生成器索要一个数时,生成器就会执行,直至出现yield语句时,生成器把yield的参数传给你,之后生成器就不会往下继续运行。当向生成器索要下一个数时,它会从上次的状态开始运行,直至出现yield语句时把参数传给你,然后停下。如此反复,直至退出函数为止。
当在pyton程序中定义个函数时, 如果使用了关键字yield那么这个函数就是一个生成器,它的执行会和其他普通的函数有很多不同, 函数返回的是一个对象, 而不是平常函数所用的retum语句那样,能得到结果值。如果想取得值,还需要调用next( ) 函数,例如在下面的演示代码中,每当调用次迭代器的next( ) 函数,生成器函数便会运行到yield 位置,返回yield后面的值,并且在这个地方暂停,所有的状态都会被保持住,直到下次next( ) 函数被调用或者碰到异常循环时才退出。
c = h() #h()包含了yield关键字
#返回值
c.next ()
例如在下面的实例代码中,演示了使用yield生成器的过程。
def fib (max): #定义方法fib()
a,b = 1,1 #为变量a和b赋值为1
while a < max: #如果a小于max
yield a #当程序运行到yield这行时就不会继续往下执行
a, b=b,a+b
print (" 奥运会金牌榜的变化")
for n in fib(15): #遍历15以内的值
print (n)
在上述实例代码中,当程序运行到yield这行时就不会继续往下执行,而是返回一个包含当前函数所有参数状态的iterator对象。目的就是为了第二次被调用时,能够访问到函数所有的参数值都是第一次访问时的值,而不是重新赋值。当程序第一次调用时:
yield a #这时a,b值分别为1,1,当然,程序也在执行到这行时,返回
当程序第二次调用时,从前面可知,当第一次调用时,a,b=1,1, 那么第二次调用时(其实就是调用第一次返回的iterator对象的next( ) 方法),程序跳到yield语句处,当执行“a,b= b,a+b”语句时,此时值变为: a,b=1,(1+1)=>a,b=1,2。 然后程序继续执行while循环,这样会再一次碰到yielda语句,也是像第一次那样, 保存函数所有参数的状态,返回一个包含这
些参数状态的iterator对象。然后等待第三次调…执行后会输出:
二、创建生成器
根据本章前面内容的学习可知,在Python程序中可以使用关键字yield 将一个函数定义为一个生成器。所以说生成器也是个函数, 能够生成个值的序列,以便在迭代中使用。例如在下面的实例代码中,演示了yield 生成器的运行机制:
def shengYield(n): #定义方法shengYield()
while n > 0: # 如果n大于0则开始循环
print("开始生成...:") # 定义一个生成器
yield n
print("完成一次...:") # 生成初始值的不断递减的数字序列
n -= 1
if __name__ == '__main__': #当模块被直接运行时,以下代码块会运行,当模块是被导入时不被运行。
for i in shengYield(4): # 遍历4次
print("遍历得到的值:",i)
print()
sheng_yield = shengYield(3)
print('已经实例化生成器对象')
sheng_yield.__next__() # 直接遍历自己创建的生成器
print("第二次调用__next__()方法:")
sheng_yield.__next__() # 手工方式获取生成器产生的数值序列
在上述实例代码中,自定义了一个递减数字序列的生成器,每次调用时都会生成一个从调用时所提供值为初始值的不断递减的数字序列。生成对象不但可以直接被for循环语句遍历,而且也可以进行手工遍历,在上述最后两行代码中便是使用的手工遍历方式。第一次使用for循环语句时直接遍历自己创建的生成器,第二次用手工方式获取生成器立生的数值序列。执行后会输出:
通过上述实例的实现过程可知,当在生成器中包含yield语句时,不但可以用for直接遍历,而且也可以使用手工方式调用其方法__next__()进行遍历。在Python程序中,yield 语句是生成器中的关键语句,生成器在实例化时并不会立即执行,而是等候其调用方法__next__()才开始运行,并且当程序运行完yield语句后就会保持当前状态并且停止运行,等待下一次遍历时才恢复运行。
在上述实例的执行结果中,在空行之后的输出“已经实例化生成器对象”的前面,已经实例化了生成器对象,但是生成器并没有运行(没有输出“开始生成”)。当第一次手工调用方法__next__()后, 才输出“开始生成”提示,这说明生成器已经开始运行,并且在输出“第二次调用__next__()方法:”文本前并没有输出“完成一次”文本,这说明yield语句在运行之后就立即停止了运行。在第二次调用方法__next__()后, 才输出“完成一次…”的文本提示,这说明从yield语句之后开始恢复运行生成器。
三、生成器的第一 次调用
在Python程序中,通过yield 语句可以使函数成为生成器,返回相关的值,并即时接受调用者传来的数值。但是读者需要注意的是,当在第一次调用生成器时, 不能传送给生成器None值以外的值。否则会引发销识。 例如在下面的实例代码中,演示了不调用生成器的具体过程:
def shengYield(n): #定义方法shengYield()
while n>0: #如果n大于0则开始循环
rcv = yield n #通过"rcv"来接收调用者传来的值
n -=1 #生成初始值的不断递减的数字序列
if rcv is not None:
n = rcv
if __name__ == '__main__':# 当模块被直接运行时,以下代码块会运行,当模块是被导入时不被运行。
sheng_yield = shengYield(2) #开始遍历时从默认值2开始递减并输出
print(sheng_yield.__next__())
print(sheng_yield.__next__())
print('将接力棒传递给另一个人,重新开始跑。') #传给生成器一个值,重新初始化生成器
print (sheng_yield.send(11)) #当重新传一个值11给生成器
print (sheng_yield.__next__()) #得到一个从11开始递减的遍历
在上述实例代码中,实现了一个可以接收调用者传来的值并重新初始化生成器生成值的过程。首先定义了一个生成器函数,其中yield语句为“rev=yield n”,通过“rcv”来接收调用者传来的值。如果在调用时只提供了一个值,就会从这个值开始递减生成序列。程序运行后,在开始遍历时从2开始递减并输出,当重新传一个值11给生成器时,会得到一个从11开始递减的遍历。执行后会输出:
四、使用协程重置生成器序列
在Python程序中,可以使用方法send( ) 重置生成器的生成序列,这被称为协程。协程是一种解决程序并发的基本方法,如果采用般的方法来实现生产 者与消费者这个传统的并发与同步程序设计问题,则需要考虑很多复杂的问题。但是如果通过生成器实现的协程这种方式,便可以很好地解决这个问题。例如在下面的实例代码中,演示了使用协程重置生成器序列的过程。
def xie(): #方法xie()代表生产者模型
print('其他队员等待接收处理任务...')
while True: #每个循环模拟发送一个任务给消费者模型(生成器)
data = (yield)
print('收到任务: ',data)
def producer () : #方法producer()代表消费者模型
c = xie() #调用函数xie()来处理任务
c.__next__()
for i in range(3): #遍历3个任务
print(' 游泳名将x杨发送一个任务...','任务%d'%i)
c.send('任务号%d'%i) #发送任务
if __name__ == '__main__':
producer()
在上述实例代码中,演示了一个简单的生产者与消费者编程模型的实现过程。通过定义两个函数xie( )和 producer ( )分别代表消费者和生产者模型,而其中消费者模型实际是一个生成器。在生产者模型函数中每个循环模拟发送一个任务给消费者模型(生成器),而生成器可以调用相关函数来处理任务。这是通过yield语句的“停止”特性来完成这一任务的。程序在运行时,每次的发送任务都是通过调用生成器函数send( )实现的,收到任务的生成器会执行相关的函数调用并完成子任务。执行后会输出:
最后
以上就是诚心皮皮虾为你收集整理的自学Python 32 生成器:边循环边计算Python 生成器:边循环边计算一、生成器的运行机制二、创建生成器三、生成器的第一 次调用四、使用协程重置生成器序列的全部内容,希望文章能够帮你解决自学Python 32 生成器:边循环边计算Python 生成器:边循环边计算一、生成器的运行机制二、创建生成器三、生成器的第一 次调用四、使用协程重置生成器序列所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复