2.1.9 Python生成器

生成器(generator),被认为是Python的高级编程技能,想要深入的学习Python,生成器是必须要了解的。
生成器和迭代器有点类似,但是又完全不相同。
1, 了解生成器
>>> my_first = (x*x for x in range(4)) #生成器
>>> my_list = [x*x for x in range(4)] #列表
>>> dir(my_first)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', ' __iter__ ', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', ' next ', 'send', 'throw']
两者的区别在于前者是方括号“[]”后者是圆括号“()”,虽然是细小的差别,但是结果完全不一样。
在my_first中可以看到__iter__和next,说明生成器是 可迭代的。
>>> for i in my_first :
print i

0
1
4
9
>>> for i in my_first :
print i

>>>
第一次循环的时候,可以依次遍历出数值,第二次遍历为空(因为游标已经移动到最后),这也是迭代器的特性。
>>> for i in my_list :
print i

0
1
4
9
>>> for i in my_list :
print i

0
1
4
9
>>>
列表每次遍历都会依次输出值。
在这能看到生成器把列表解析中的“[]”换成“()”,这仅仅是生成器的一种表现形式和基本使用方法,仿照列表解析式的命名,可以称之为“ 生成器解析式
生成器解析式有很多用途,在不少地方可以替代列表解析,特别是针对 大数据的时候,Python处理列表时,将 全部数据都读入到内存,而迭代器( 生成器是迭代器)的优势就在于只将所需要的读入内存里,因此生成器解析式比列表解析式 少占内存
求1到10以内的平方和:
>>> sum(i*i for i in range(10)) #生成器
285
>>> sum([i*i for i in range(10)]) #列表
285
2, 生成器怎么执行
yield关键词是生成器的标志。
>>> def test():
yield 0
yield 1
yield 2

>>> t = test()
>>> type(t)
<type ' generator '>
>>> dir(t)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']
看到了__iter__()和next(),说明它是迭代器。返回值是一个生成器(generator)类型的对象
这个生成器在定义过程中并没有显化地使用__inter__()和next(),而是只要用了 yield语句,那个普通函数就神奇般地成为了生成器,也就具备了迭代器的功能特性。
>>> t.next() #生成器才开始执行,遇到了第一个yield语句,将值返回,并暂停执行
0
>>> t.next() #从上次暂停的位置开始,继续向下执行,遇到yield语句,将值返回,又暂停。
1
>>> t.next() #同上
2
>>> t.next() #从上面的挂起位置开始,但是后面没有可执行的了,于是next()发出异常。

Traceback (most recent call last):
File "<pyshell#39>", line 1, in <module>
t.next()
StopIteration
遍历上面代码会发现 yield除了作为生成器的标志之外,还有一个功能就是 返回值
3, yield和return的区别
>>> def test_return(n):
print "开始进入代码"
while n > 0:
print "return之前"
return n
n -= 1
print "return之后"

>>> r = test_return(3)
开始进入代码
return之前
>>> r
3
从函数被调用的过程可以清晰看出,从r=test_return(3)开始,就执行函数体内容了,当遇到return的时候执行该语句,将值返回,然后就结束函数体内的执行,所以return后面的语句根本没有执行。
修改return为yield试试:
>>> def test_yield(n):
print "开始进入代码"
while n > 0:
print "return之前"
yield n
n -= 1
print "return之后"

>>> y = test_yield(3)
>>> y.next() #开始执行
开始进入代码
return之前
3 #遇到yield,返回值,并暂停
>>> y.next() #从上次暂停位置开始继续执行
return之后
return之前
2 #遇到yield,返回值,并暂停
>>> y.next() #从上次暂停位置开始继续执行
return之后
return之前
1
一般的函数,都是止于 return。作为生成器的函数,由于有了 yield,遇到它则程序挂起,如果在之后还有 return,遇到它就直接抛出SoptIteration异常而中止迭代。
至此,已经明确,一个函数中,只要包含了 yield语句,它就是生成器,也是迭代器。

send()方法运行后能够为生成器提供值,必须在生成器运行后并挂起才能使用,即yield至少被执行一次。
throw(type,value=None,traceback=None):用于在生成器内部(生成器的当前挂起处或未启动时在定义处)抛出一个异常(在yield表达式中)。
close():调用时不用参数,用于关闭生成器。
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值