python中yield语句的作用,yield函数怎么用python

  python中yield语句的作用,yield函数怎么用python

  目录1。背景2。如何生成斐波那契数列1的列表?只需输出斐波那契数列2的前n个数的列表。输出斐波那契数列2的前n个数的列表。通过iterable objects 4迭代列表。第三版本列表5。使用yield的第四个版本。7.清单8。类的定义和清单9。产量的另一个例子。3.下一站的位置。

  1.背景你可能听说过一个带yield的函数在Python中被称为生成器。什么是发电机?

  让我们抛开生成器,用一个常见的编程话题来展示yield的概念。

  2.如何生成斐波那契数列?斐波那契数列是一个非常简单的递归数列,除了第一个和第二个数以外的任何数都可以通过前两个数相加得到。用计算机程序输出斐波那契数列的前N个数是一个非常简单的问题。许多初学者可以很容易地编写以下函数:

  1.只需输出斐波那契数列的前n个数#!/usr/bin/python

  # -*-编码:UTF-8 -*-

  def fab(最大):

  n,a,b=0,0,1

  当n最大值:

  打印b

  a,b=b,a b

  n=n 1

  Fab(5)执行上面的代码,我们可以得到下面的输出:

  一个

  一个

  2

  三

  五

  结果没有问题,但是有经验的开发者会指出,在fab函数中直接用print打印数字会导致这个函数的复用性很差,因为fab函数返回None,其他函数无法获得这个函数生成的数列。

  为了提高fab函数的可重用性,最好返回一个列表,而不是直接打印出系列。下面是重写后的第二个版本的fab函数:

  2.输出斐波那契数列的前N个数的第二版例子

  #!/usr/bin/python

  # -*-编码:UTF-8 -*-

  def fab(最大):

  n,a,b=0,0,1

  L=[]

  当n最大值:

  l .追加(b)

  a,b=b,a b

  n=n 1

  返回L

  对于fab(5)中的n:

  n print重写的fab函数可以通过返回列表来满足复用性的要求,但是比较有经验的开发者会指出这个函数占用的内存会随着参数max的增加而增加。如果想控制内存占用,最好不要用List。

  保存中间结果,但是遍历iterable对象。例如,在Python2.x中,代码:

  清单3。通过iterable object: pass对范围(1000)中的I进行迭代将得到一个1000元素的列表,而代码:

  对于xrange中的I(1000):pass不生成1000个元素的列表,而是在每次迭代中返回下一个值,占用内存空间很小。因为xrange返回的不是一个列表,而是一个iterable对象。

  注意:python3中没有xrange()。在python3中,range()是xrange()。你可以在python3中检查range()的类型。它已经是一个类“范围”而不是列表。毕竟这个还需要优化。

  使用iterable,我们可以将fab函数重写为支持iterable的类。这是Fab的第三个版本:

  清单4。第三个版本的示例

  #!/usr/bin/python

  # -*-编码:UTF-8 -*-

  Fab类(对象):

  def __init__(self,max):

  self.max=max

  self.n,self.a,self.b=0,0,1

  def __iter__(self):

  回归自我

  定义下一个(自己):

  if self.n self.max:

  r=自我b

  self.a,self.b=自我

  self.n=self.n 1

  返回r

  引发StopIteration()

  对于Fab(5)中的n:

  print nFab类通过next()不断返回序列的下一个数字,内存占用始终是恒定的:

  一个

  一个

  2

  三

  但是用class重写的这个版本,代码远不如第一个版本的fab函数简洁。如果我们想保持第一版fab函数的简单性,同时获得iterable的效果,yield就派上用场了:

  清单5。使用yield的第四版实例

  #!/usr/bin/python

  # -*-编码:UTF-8 -*-

  def fab(最大):

  n,a,b=0,0,1

  当n最大值:

  产量b #使用产量

  #打印b

  a,b=b,a b

  n=n 1

  对于fab(5)中的n:

  与第一版相比,Print的fab第四版只是将print b改为yield b,保持了简洁性,达到了iterable的效果。

  调用第四版的fab和调用第二版的fab一模一样:

  一个

  一个

  2

  三

  5简单来说,yield的作用就是把一个函数变成一个生成器。带yield的函数不再是普通函数,Python解释器会把它当成生成器。调用fab(5)不会执行fab函数,而是返回一个iterable对象!执行for循环时,每次都会执行fab函数内部的代码。当到达产量b时,fab函数将返回一个迭代值。在下一次迭代中,代码将从yield b的下一条语句开始继续执行,函数的局部变量看起来与上次中断执行前完全一样,因此函数将继续执行,直到再次满足yield。

  也可以手动调用fab(5)的next()方法(因为fab(5)是一个生成器对象,它有next()方法),这样我们可以更清楚的看到fab的执行流程:

  6.实现进程

  f=fab(5)

  f.next()

  一个

  f.next()

  一个

  f.next()

  2

  f.next()

  三

  f.next()

  五

  f.next()

  回溯(最近一次呼叫):

  文件“”中的第1行

  停止迭代

  当函数执行完成时,生成器自动抛出StopIteration异常,指示迭代完成。在for循环中,不需要处理StopIteration异常,循环将正常结束。

  我们可以得出以下结论:

  有屈服的函数是生成元,不同于普通函数。生成一个生成器看起来像一个函数调用,但它不会执行任何函数代码,直到对它调用next()为止(next()将在for循环中自动调用)。虽然执行流还是按照函数流执行,但是每执行一条yield语句就会被中断,返回一个迭代值。下一次执行将从下一个yield语句继续。看起来好像一个函数在正常执行过程中被yield中断了几次,每次中断都会通过yield返回当前的迭代值。

  收益率的好处显而易见。通过将函数重写为生成器,可以获得迭代的能力。与通过保存类的实例状态来计算next()的值相比,不仅代码简洁,而且执行过程异常清晰。

  如何判断一个函数是不是特殊的生成函数?Isgeneratorfunction可以用来判断:

  清单7。使用isgeneratorfunction确定从Inspect导入IsGenerator函数

  isgeneratorfunction(fab)

  True要注意区分fab和fab(5),fab(5)是生成器函数,而fab(5)是调用fab返回的生成器,就像类的定义和类的实例的区别一样:

  清单8。类的定义和类的实例导入类型

  isinstance(fab,类型。GeneratorType)

  错误的

  isinstance(fab(5),类型。GeneratorType)

  Truefab不能迭代,而fab(5)可以迭代:

  从集合导入迭代

  isinstance(fab,Iterable)

  错误的

  isinstance(fab(5),Iterable)

  false每次调用fab函数时,都会生成一个新的生成器实例,并且每个实例不会相互影响:

  f1=fab(3)

  f2=fab(5)

  打印“f1:”,f1.next()

  f1: 1

  打印“f2:”,f2.next()

  f2: 1

  打印“f1:”,f1.next()

  f1: 1

  打印“f2:”,f2.next()

  f2: 1

  打印“f1:”,f1.next()

  f1: 2

  打印“f2:”,f2.next()

  f2: 2

  打印“f2:”,f2.next()

  f2: 3

  打印“f2:”,f2.next()

  F2:5返回的功能

  在一个生成器函数中,如果没有返回,默认情况下会一直执行到函数完成。如果在执行过程中返回,会直接抛出StopIteration终止迭代。

  另一个例子

  yield的另一个例子来自文件读取。如果直接在file对象上调用read()方法,会导致不可预知的内存占用。一个好的方法是使用固定长度的缓冲区不断地读取文件的内容。使用yield,我们不再需要编写迭代类来读取文件,因此我们可以轻松地读取文件:

  清单9。产量的另一个例子

  def read_file(fpath):

  BLOCK_SIZE=1024

  用open(fpath, rb )作为f:

  虽然正确:

  block=f.read(BLOCK_SIZE)

  如果阻止:

  屈服块

  否则:

  以上只是简单介绍了yield的基本概念和用法。yield在Python 3中有更强大的用法,我们将在下一篇文章中讨论。

  3.下一个停止位置def foo():

  打印(开始.)

  虽然正确:

  res=收益率4

  打印( res:,res)

  g=foo()

  打印(下一个(g))

  打印( **20)

  打印(下一个(g))输出:

  开始.

  四

  ********************

  res:无

  4解释代码运行顺序,相当于代码的单步调试:

  1.程序开始执行后,foo函数不会因为foo函数中的yield关键字而实际执行,而是会先获得一个生成器g(相当于一个对象)。2.直到调用下一个方法,foo函数才正式开始执行,先执行foo函数中的print方法,然后进入while循环。3.程序遇到yield关键字,然后把yield当成return。返回4后,程序停止,不执行res赋值操作。这时,next(g)语句完成,于是输出前两行(第一行是上面print above的结果,第二行是return的结果,返回执行print(next(g))的结果。4.程序执行打印(“*”20)并输出20。5.它再次开始执行下面的打印(next(g))。这次和上一次类似,不同的是这次是从刚才下一个程序停止的地方开始执行,也就是要执行res的赋值操作。这时需要注意的是,此时赋值操作的右边没有值(因为那一个刚用return出去,赋值操作的左边没有传参数),所以此时res赋值为None,那么下面的输出就是res:None,6。程序将继续执行一会儿,并再次遇到让步。这时它也返回4,然后程序停止。print函数输出的4就是这个return输出的4。在这里,你可能会明白收益率和回报的关系和区别。带yield的函数是生成器,不是函数。这个生成器有一个函数叫next函数,next相当于“下一步”生成哪个数。next这次开始的位置在last next停止的位置旁边执行。所以调用next的时候,生成器并不是从foo函数的开头开始,而只是从上一步停止的地方开始,遇到yield后再返回要生成的数,这一步就结束了。

  def foo():

  打印(开始.)

  虽然正确:

  res=收益率4

  打印( res:,res)

  g=foo()

  打印(下一个(g))

  打印( **20)

  Print(g.send(7))看看这个生成器的send函数的另一个例子。此示例替换上面示例的最后一行,并输出结果:

  开始.

  四

  ********************

  决议:7

  4先说一下send函数的概念:此时你应该注意到了上面那个紫色的字,为什么上面的res的值是None,而这个变成了7。为什么?这是因为,send向res发送一个参数,因为如上所述,当你返回的时候,并没有给res赋值4,下次还要继续赋值操作,所以你要给None赋值。如果使用send,那么在开始执行的时候,要按照上一次执行(返回4之后),先给res赋值7,然后执行next的函数,遇到下一个yield,返回结果,结束。

  5.程序执行g.send(7),程序会继续从yield关键字的行往下运行,send会把值7赋给res变量。6.因为send方法包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环。7.程序执行再次遇到yield关键字后,yield将返回以下值,程序将再次暂停,直到再次调用下一个方法或send方法。连接:

  https://www.runoob.com/w3cnote/python-yield-used-analysis.html

  来自

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: