温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何在Python中定义生成器

发布时间:2021-04-30 16:48:19 来源:亿速云 阅读:257 作者:Leah 栏目:开发技术

这篇文章给大家介绍如何在Python中定义生成器,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

python的五大特点是什么

python的五大特点:1.简单易学,开发程序时,专注的是解决问题,而不是搞明白语言本身。2.面向对象,与其他主要的语言如C++和Java相比, Python以一种非常强大又简单的方式实现面向对象编程。3.可移植性,Python程序无需修改就可以在各种平台上运行。4.解释性,Python语言写的程序不需要编译成二进制代码,可以直接从源代码运行程序。5.开源,Python是 FLOSS(自由/开放源码软件)之一。

一、什么是生成器

在Python中,由于受到内存的限制,列表容量肯定是有限的。例如我们创建一个包含一亿个元素的列表,Python首先会在内存中开辟足够的空间来存储这个包含一亿个元素的列表,然后才允许用户去使用这个列表,这就可能会导致以下问题:

  1、内存中没有足够的内存空间开存储这个列表,从而导致列表无法创建

  2、即使列表成功创建,然而仍会消耗很长的时间,导致程序效率低下

  3、若用户只想访问列表前面的几个元素,则后面列表绝大多数元素占用的空间就都白白浪费了

为了有效解决以上的问题,Python中引入了一种“一边循环,一边计算”的新机制,即当用户需要使用某个对象时,Python才根据事先设计好的规则开辟内存空间创建这个对象供用户使用,而不是像列表一样事先将所有的对象都创建完毕之后再提供给用户使用。这种机制在Python中成为生成器(generator)。

二、生成器的创建

A、生成器推到式

与列表推到式类似,只不过生成器推导式使用()而非[],并且最终返回的是生成器而非列表

g=((i+2)**2 for i in range(2,30)) #g是一个生成器
print(g) #g为空,里面包含任何元素

运行结果:

<generator object <genexpr> at 0x0000000002263150>

B、yield关键字

在一个函数定义中包含yield关键字,则这个函数就不再是一个普通的函数,而是一个生成器(generator)

[说明]:yield指令可以暂停一个函数并返回其中间结果,使用该指令的函数将保存执行环境,并在必要时恢复

def fib(max):
  n,a,b=0,0,1
  while n<max:
    #print(b)
    yield b
    a,b=b,a+b
    n+=1
  return 'done'
f=fib(6)
print(f)

运行结果:

<generator object fib at 0x0000000002553150>

[注]:普通函数和变成生成器的函数的不同:

普通函数是顺序执行的,遇到return或是最后一行函数语句就返回。而变成生成器的函数在每次调用__next__()方法时执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

f=fib(6)
print(f)
print(f.__next__())
print(f.__next__())
print('暂停一下')
print(f.__next__())
print(f.__next__())

运行结果:

<generator object fib at 0x00000000025631A8>
1
1
暂停一下
2
3

三、生成器方法(参考:伯乐在线)

1.close()方法:手动关闭生成器函数,后面的调用会直接返回StopIteration异常

def func():
  yield 1
  yield 2
  yield 3
g=func()
g.__next__()
g.close() #手动关闭生成器
g.__next__() #关闭后,yield 2和yield 3语句将不再起作用

运行结果:

Traceback (most recent call last):
  File "E:\py3Demo\Hello\generatorDemo.py", line 9, in <module>
    g.__next__() #关闭后,yield 2和yield 3语句将不再起作用
StopIteration

2.__next__()方法:返回生成器的下一次调用

def func():
  n=1
  for i in range(3):
    yield n
    n+=1
c=func()
a1=c.__next__()
a2=c.__next__()
a3=c.__next__()

[流程解释]:

对于普通的生成器,第一个__next__()方法的调用相当于启动生成器,此时会从生成器函数的第一行开始执行,直到第一次执行完yield语句(第四行)后,跳出生成器函数。

当调用第二个__next__()方法后,会重新进入生成器函数,并从yield语句的下一条语句(第五行)开始执行,直到重新运行到yield语句,执行后再次跳出生成器函数。

后面的__next__()方法调用以此类推

3.send()方法:接受外部传入的一个变量,并根据变量内容计算结果返回到生成器函数中

[注]:

(1)send()方法和__next__()方法相似,区别在于send()方法可以传递给yield表达式值,而__next__()方法不能传递特定的值,只能传递None给yield表达式,因此可以将generator.__next__()理解为generator.send(None)

(2)第一次调用生成器函数时,必须使用__next__()语句或是send(None),不能使用send发送一个非None的值给生成器函数,否则会出错,因为没有yield语句来接收这个值

def gen():
  value=0
  while True:
    receive=yield value
    if receive=='end':
      break
    value='Got:%s' %receive
g=gen()
print(g.__next__()) #或是print(g.send(None)),从而启动生成器
print(g.send('aaa'))
print(g.send(3))
print(g.send('end'))

运行结果:

0
Got:aaa
Got:3
Traceback (most recent call last):
  File "E:\py3Demo\Hello\generatorDemo.py", line 13, in <module>
    print(g.send('end'))
StopIteration

[流程解释]:

a.通过g.send(None)或g.__next__()启动生成器函数,并执行到第一个yield语句结束的位置并将函数挂起。此时执行完了yield语句,但是没有给receive赋值,因此yield value会输出value的初始值0

b.g.send('aaa')先将字符串‘aaa'传入到生成器函数中并赋值给receive,然后从yield语句的下一句重新开始执行函数(第五句),计算出value的值后返回到while头部开始新一轮的循环,执行到yield value语句时停止,此时yield value会输出‘Got:aaa',然后挂起

c.g.send(3)重复步骤b,最后输出结果为‘Got:3'

d.g.send('end')会使程序执行break然后跳出循环,从而函数执行完毕,得到StopIteration异常

4.throw()方法:向生成器发送一个异常。

def gen():
  while True:
    try:
      yield 'normal value' #返回中间结果,此处的yield和return的功能相似
      yield 'normal value2'
      print('I am here')
    except ValueError:
      print('We got ValueError')
    except Exception:
      print('Other errors')
      break
g=gen()
print(g.__next__())
print(g.throw(ValueError))
print(g.__next__())
print(g.throw(TypeError))

运行结果:

Traceback (most recent call last):
  File "E:\py3Demo\Hello\generatorDemo.py", line 17, in <module>
    print(g.throw(TypeError))
StopIteration
normal value
We got ValueError
normal value
normal value2
Other errors

[解释]:

a.print(g.__next__())会输出normal value,并停在yield 'normal value2'之前

b.由于执行了g.throw(ValueError),所以回跳过后续的try语句,即yield ‘normal value2'不会执行,然后进入到except语句,打印出‘We got ValueError'。之后再次进入到while语句部分,消耗一个yield,输出normal value

c.print(g.__next__())会执行yield ‘normal value2'语句,并停留在执行完该语句后的位置

d.g.throw(TypeError)会跳出try语句,因此print('I am here')不会被执行,然后打印‘Other errors',并执行break语句跳出while循环,然后到达程序结尾,打印StopIteration异常的信息

四、生成器的运用

import time
def consumer(name):
  print('%s准备吃包子啦!' %name)
  while True:
    baozi=yield #接收send传的值,并将值赋值给变量baozi
    print('包子[%s]来了,被[%s]吃了!' %(baozi,name))
def producer(name):
  c1=consumer('A') #把函数变成一个生成器
  c2=consumer('B')
  c1.__next__()#调用这个方法会走到yield处暂时返回
  c2.__next__()
  print('开始准备做包子啦!')
  for i in range(10):
    time.sleep(1)
    print('做了一个包子,分成两半')
    c1.send(i)
    c2.send(i)
producer('Tomwenxing')

关于如何在Python中定义生成器就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI