什么是python生成器?

发布时间:2022-04-25 09:38:31 人气:23 作者:多测师

  通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

  所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator

  生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

  生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器

  python中的生成器

  要创建一个generator,有很多种方法,第一种方法很简单,只有把一个列表生成式的[]中括号改为()小括号,就创建一个generator

  举例如下:

  #列表生成式

  lis = [x*x for x in range(10)]

  print(lis)

  #生成器

  generator_ex = (x*x for x in range(10))

  print(generator_ex)

  结果:

  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  at 0x000002A4CBF9EBA0>

  那么创建list和generator_ex,的区别是什么呢?从表面看就是[ ]和(),但是结果却不一样,一个打印出来是列表(因为是列表生成式),而第二个打印出来却是 at 0x000002A4CBF9EBA0>,那么如何打印出来generator_ex的每一个元素呢?

  如果要一个个打印出来,可以通过next()函数获得generator的下一个返回值:

  #生成器

  generator_ex = (x*x for x in range(10))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  print(next(generator_ex))

  结果:

  Traceback (most recent call last):

  File "列表生成式.py", line 42, in

  print(next(generator_ex))

  StopIteration

  大家可以看到,generator保存的是算法,每次调用next(generaotr_ex)就计算出他的下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration的错误,而且上面这样不断调用是一个不好的习惯,正确的方法是使用for循环,因为generator也是可迭代对象:

什么是python生成器?

  #生成器

  generator_ex = (x*x for x in range(10))

  for i in generator_ex:

  print(i)

  结果:

  所以我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心StopIteration的错误,generator非常强大,如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

  比如著名的斐波那契数列,除第一个和第二个数外,任何一个数都可以由前两个相加得到:

  1,1,2,3,5,8,12,21,34.....

  斐波那契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

  #fibonacci数列

  def fib(max):

  n,a,b =0,0,1

  while n < max:

  a,b =b,a+b

  n = n+1

  print(a)

  return 'done'

  a = fib(10)

  print(fib(10))

  a,b = b ,a+b 其实相当于 t =a+b ,a =b ,b =t ,所以不必写显示写出临时变量t,就可以输出斐波那契数列的前N个数字。上面输出的结果如下:

  仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

  也就是说上面的函数也可以用generator来实现,上面我们发现,print(b)每次函数运行都要打印,占内存,所以为了不占内存,我们也可以使用生成器,这里叫yield。如下:

  def fib(max):

  n,a,b =0,0,1

  while n < max:

  yield b

  a,b =b,a+b

  n = n+1

  return 'done'

  a = fib(10)

  print(fib(10))

  但是返回的不再是一个值,而是一个生成器,和上面的例子一样,大家可以看一下结果:

  那么这样就不占内存了,这里说一下generator和函数的执行流程,函数是顺序执行的,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次被next()调用时候从上次的返回yield语句处急需执行,也就是用多少,取多少,不占内存。

  def fib(max):

  n,a,b =0,0,1

  while n < max:

  yield b

  a,b =b,a+b

  n = n+1

  return 'done'

  a = fib(10)

  print(fib(10))

  print(a.__next__())

  print(a.__next__())

  print(a.__next__())

  print("可以顺便干其他事情")

  print(a.__next__())

  print(a.__next__())

  结果:

  可以顺便干其他事情

  在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

  def fib(max):

  n,a,b =0,0,1

  while n < max:

  yield b

  a,b =b,a+b

  n = n+1

  return 'done'

  for i in fib(6):

  print(i)

  结果:

  但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果拿不到返回值,那么就会报错,所以为了不让报错,就要进行异常处理,拿到返回值,如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

  def fib(max):

  n,a,b =0,0,1

  while n < max:

  yield b

  a,b =b,a+b

  n = n+1

  return 'done'

  g = fib(6)

  while True:

  try:

  x = next(g)

  print('generator: ',x)

  except StopIteration as e:

  print("生成器返回值:",e.value)

  break

  结果:

  generator: 1

  generator: 1

  generator: 2

  generator: 3

  generator: 5

  generator: 8

  生成器返回值: done

  还可以通过yield实现在单线程的情况下实现并发运算的效果

  由上面的例子我么可以发现,python提供了两种基本的方式

  生成器函数:也是用def定义的,利用关键字yield一次性返回一个结果,阻塞,重新开始

  生成器表达式:返回一个对象,这个对象只有在需要的时候才产生结果

  以上内容为大家介绍了什么是python生成器?,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注多测师。https://www.e70w.com/xwzx/


返回列表
在线客服
联系方式

热线电话

17727591462

上班时间

周一到周五

二维码
线