python生成式与生成器,python代码生成器
这篇文章给大家带来了一些关于python的知识,包括生成器的概念,生成器的执行过程,yield和generator方法。来看看吧,希望对你有帮助。
推荐:python视频教程
这篇文章给大家带来了一些关于Python的知识,包括生成器的概念,生成器的执行过程,yield和generator方法。来看看吧,希望对你有帮助。
00-1010 generator(英文:generator)是一个令人着迷的东西,经常被认为是Python的高级编程技能。但是,我还是很
我很高兴在这里和读者——讨论这个话题,即使你可能是个初学者,因为我相信阅读这个教程的目的绝不是把自己限制在初学者的水平。要成为蟒蛇高手,必须有一颗桀骜不驯的心。然后,了解发电机。
还记得上一节的“迭代器”吗?生成器和迭代器有一定的渊源关系。生成器必须是迭代的,这是真的,它不只是
迭代器,但除此之外,它并没有太多其他用途,所以我们可以把它理解为一个非常方便的自定义迭代器。
00-1010 my _ generator=(x * x for x in range(4))。这是不是很像列表解析?仔细看,这不是列表,如果这是列表:
My_list=[x*x for x in range(4)]以上两者之差为[]或()。虽然是细微的差别,但结果却完全不同。
目录(我的生成器)
[__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 ,
下一个,
Send , throw]为了便于观察,我重新整理了上面的结果。有没有在迭代器中找到必要的方法__inter__()和next(),说明是迭代器?如果是迭代器,可以使用for循环依次读取其值。
因为我在我的世代:
.打印I
.
0
任何人
四
九
因为我在我的世代:
.打印I
.第一次循环时,依次读出并打印my_generator中的值,但再次读取时,发现没有结果。这个特性正是迭代器所具备的。
如果对于那个列表,它是不同的:
因为我在我的名单上
.打印I
.
0
任何人
四
九
因为我在我的名单上
.打印I
.
0
任何人
四
生成器只是在列表解析中用()替换[]吗?这只是发电机的一种表现形式和用法,仿照
解析表达式的命名可称为“生成器解析表达式”(或:生成器推导和生成器表达式)。
发电机的用途很多,很多地方替换清单都是不错的选择。特别是对于大量的值,前面提到过,列表占用内存比较多,迭代器(生成器就是迭代器)的优点是占用内存比较少。因此,没有必要将生成器(或迭代器)实例化为一个列表,直接对其进行操作,以显示其迭代优势。例如:
sum(i*i for i in range(10))
85注意上面的sum()运算。不要以为里面少了一个括号。就是这么写的。是不是很迷人?如果名单,你
不得不:
>
>>> sum([i*i for i in range(10)])通过生成器解析式得到的生成器,掩盖了生成器的一些细节,并且适用领域也有限。下面就要剖析生成器的内部,深入理解这个魔法工具。285
3. 定义和执行过程
yield 这个词在汉语中有“生产、出产”之意,在 Python 中,它作为一个关键词(你在变量、函数、类的名称中就不能用这个了),是生成器的标志。
>>> def g():建立了一个非常简单的函数,跟以往看到的函数唯一不同的地方是用了三个 yield 语句。然后进行下面的操作:... yield 0
... yield 1
... yield 2
...
>>> g
<function g at 0xb71f3b8c>
>>> ge = g()上面建立的函数返回值是一个生成器(generator)类型的对象。>>> ge
<generator object g at 0xb7200edc>
>>> type(ge)
<type 'generator'>
>>> dir(ge)在这里看到了 __iter__() 和 next() ,说明它是迭代器。既然如此,当然可以:['__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']
>>> ge.next()从这个简单例子中可以看出,那个含有 yield 关键词的函数返回值是一个生成器类型的对象,这个生成器对象就是迭代器。0
>>> ge.next()
1
>>> ge.next()
2
>>> ge.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
我们把含有 yield 语句的函数称作生成器。生成器是一种用普通函数语法定义的迭代器。通过上面的例子可以看出,这个生成器(也是迭代器),在定义过程中并没有像上节迭代器那样写 __inter__() 和 next() ,而是只要用了 yield 语句,那个普通函数就神奇般地成为了生成器,也就具备了迭代器的功能特性。
yield 语句的作用,就是在调用的时候返回相应的值。详细剖析一下上面的运行过程:
1. ge = g() :除了返回生成器之外,什么也没有操作,任何值也没有被返回。
2. ge.next() :直到这时候,生成器才开始执行,遇到了第一个 yield 语句,将值返回,并暂停执行(有的称之
为挂起)。
3. ge.next() :从上次暂停的位置开始,继续向下执行,遇到 yield 语句,将值返回,又暂停。
4. gen.next() :重复上面的操作。
5. gene.next() :从上面的挂起位置开始,但是后面没有可执行的了,于是 next() 发出异常。
从上面的执行过程中,发现 yield 除了作为生成器的标志之外,还有一个功能就是返回值。那么它跟 return 这个返回值有什么区别呢?
4. yield
为了弄清楚 yield 和 return 的区别,我写了两个函数来掩饰:
>>> def r_return(n):从函数被调用的过程可以清晰看出, rr = r_return(3) ,函数体内的语句就开始执行了,遇到 return,将值返... print "You taked me."
... while n > 0:
... print "before return"
... return n
... n -= 1
... print "after return"
...
>>> rr = r_return(3)
You taked me.
before return
>>> rr
3
回,然后就结束函数体内的执行。所以 return 后面的语句根本没有执行。这是 return 的特点
下面将 return 改为 yield:
>>> def y_yield(n):结合注释和前面对执行过程的分析,读者一定能理解 yield 的特点了,也深知与 return 的区别了。... print "You taked me."
... while n > 0:
... print "before yield"
... yield n
... n -= 1
... print "after yield"
...
>>> yy = y_yield(3) #没有执行函数体内语句
>>> yy.next() #开始执行
You taked me.
before yield
3 #遇到 yield,返回值,并暂停
>>> yy.next() #从上次暂停位置开始继续执行
after yield
before yield
2 #又遇到 yield,返回值,并暂停
>>> yy.next() #重复上述过程
after yield
before yield
1
>>> yy.next()
after yield #没有满足条件的值,抛出异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
一般的函数,都是止于 return。作为生成器的函数,由于有了 yield,则会遇到它挂起,如果还有 return,遇到它就直接抛出 SoptIteration 异常而中止迭代。
#!/usr/bin/env Python运行结果如下:# coding=utf-8
def fibs(max):
"""
斐波那契数列的生成器
"""
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
if __name__ == "__main__":
f = fibs(10)
for i in f:
print i ,
$ python 21501.py用生成器方式实现的斐波那契数列是不是跟以前的有所不同了呢?大家可以将本教程中已经演示过的斐波那契数列实现方式做一下对比,体会各种方法的差异。1 1 2 3 5 8 13 21 34 55
经过上面的各种例子,已经明确,一个函数中,只要包含了 yield 语句,它就是生成器,也是迭代器。这种方式显然比前面写迭代器的类要简便多了。但,并不意味着上节的就被抛弃。是生成器还是迭代器,都是根据具体的使用情景而定。
5. 生成器方法
在 python2.5 以后,生成器有了一个新特征,就是在开始运行后能够为生成器提供新的值。这就好似生成器和“外界”之间进行数据交流。
>>> def repeater(n):当执行到 r.next() 的时候,生成器开始执行,在内部遇到了 yield n 挂起。注意在生成器函数中, n = (yield... while True:
... n = (yield n)
...
>>> r = repeater(4)
>>> r.next()
4
>>> r.send("hello")
'hello
n) 中的 yield n 是一个表达式,并将结果赋值给 n,虽然不严格要求它必须用圆括号包裹,但是一般情况都这
么做,请大家也追随这个习惯。
当执行 r.send("hello") 的时候,原来已经被挂起的生成器(函数)又被唤醒,开始执行 n = (yield n) ,也就是
讲 send() 方法发送的值返回。这就是在运行后能够为生成器提供值的含义。
如果接下来再执行 r.next() 会怎样?
>>> r.next()什么也没有,其实就是返回了 None。按照前面的叙述,读者可以看到,这次执行 r.next() ,由于没有传入任何值,yield 返回的就只能是 None.
还要注意,send() 方法必须在生成器运行后并挂起才能使用,也就是 yield 至少被执行一次。如果不是这样:
>>> s = repeater(5)就报错了。但是,可将参数设为 None:>>> s.send("how")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>> s.send(None)这是返回的是调用函数的时传入的值。5
此外,还有两个方法:close() 和 throw()
• throw(type, value=None, traceback=None):用于在生成器内部(生成器的当前挂起处,或未启动时在定
义处)抛出一个异常(在 yield 表达式中)。
• close():调用时不用参数,用于关闭生成器。
推荐学习:python视频教程以上就是一文详解python生成器的详细内容,更多请关注盛行IT软件开发工作室其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。