目录(?)[+]
http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html
首先请确信,生成器就是一种迭代器。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。另外,对于生成器的特殊语法支持使得编写一个生成器比自定义一个常规的迭代器要简单不少,所以生成器也是最常用到的特性之一。
从Python 2.5开始,[PEP 342:通过增强生成器实现协同程序]的实现为生成器加入了更多的特性,这意味着生成器还可以完成更多的工作。这部分我们会在稍后的部分介绍。
如何获取一个生成器?首先来看一小段代码:
1
2
3
4
5
6
7
|
>>>
def
get_0_1_2():
...
yield
0
...
yield
1
...
yield
2
...
>>> get_0_1_2
<function get_0_1_2 at
0x00B2CB70
>
|
我们定义了一个函数get_0_1_2,并且可以查看到这确实是函数类型。但与一般的函数不同的是,get_0_1_2的函数体内使用了关键字yield,这使得get_0_1_2成为了一个生成器函数。生成器函数的特性如下:
1
2
3
|
>>> generator
=
get_0_1_2()
>>> generator
<generator
object
get_0_1_2 at
0x00B1C7D8
>
|
1
2
|
>>> generator.
next
()
0
|
1
2
3
4
|
>>> generator.
next
()
1
>>> generator.
next
()
2
|
1
2
3
4
|
>>> generator.
next
()
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
StopIteration
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
>>>
def
fibonacci():
... a
=
b
=
1
...
yield
a
...
yield
b
...
while
True
:
... a, b
=
b, a
+
b
...
yield
b
...
>>>
for
num
in
fibonacci():
...
if
num >
100
:
break
...
print
num,
...
1
1
2
3
5
8
13
21
34
55
89
|
接下来我们来讨论一些关于生成器的有意思的话题。
1
2
3
4
5
|
>>>
def
counter(start
=
0
):
...
while
True
:
...
yield
start
... start
+
=
1
...
|
1
2
3
4
5
6
|
>>>
def
i_wanna_return():
...
yield
None
...
return
None
...
File
"<stdin>"
, line
3
SyntaxError:
'return'
with argument inside generator
|
1
2
3
4
5
6
7
8
9
10
11
|
>>>
def
play_u():
...
try
:
...
yield
1
...
yield
2
...
yield
3
...
finally
:
...
yield
0
...
>>>
for
val
in
play_u():
print
val,
...
1
2
3
0
|
1
2
3
4
5
|
>>>
def
sub_generator():
...
yield
1
...
yield
2
...
for
val
in
counter(
10
):
yield
val
...
|
1
2
3
4
5
6
7
8
|
>>>
def
sub_generator():
...
yield
1
...
yield
2
...
yield
from
counter(
10
)
File
"<stdin>"
, line
4
yield
from
counter(
10
)
^
SyntaxError: invalid syntax
|
有更多问题?请回复此文:)
协同程序(协程)一般来说是指这样的函数:
协程的特点决定了同一时刻只能有一个协同程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁、或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。
从另一方面说,协程无法并发其实也将它的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多的被拿来与常规函数进行比较,而不是与线程。当然,线程比协程复杂许多,功能也更强大,所以我建议大家牢牢地掌握线程即可:Python线程指南
这一节里我也就不列举关于协程的例子了,以下介绍的方法了解即可。
Python 2.5对生成器的增强实现了协程的其他特点,在这个版本中,生成器加入了如下方法:
1
2
3
4
5
6
7
8
9
10
|
>>>
def
repeater():
... n
=
0
...
while
True
:
... n
=
(
yield
n)
...
>>> r
=
repeater()
>>> r.
next
()
0
>>> r.send(
10
)
10
|
*别为没见到协程的例子遗憾,协程最常见的用处其实就是生成器。
这一节里我要向诸位简要介绍pipe。pipe并不是Python内置的库,如果你安装了easy_install,直接可以安装它,否则你需要自己下载它:http://pypi.python.org/pypi/pipe
之所以要介绍这个库,是因为它向我们展示了一种很有新意的使用迭代器和生成器的方式:流。pipe将可迭代的数据看成是流,类似于linux,pipe使用'|'传递数据流,并且定义了一系列的“流处理”函数用于接受并处理数据流,并最终再次输出数据流或者是将数据流归纳得到一个结果。我们来看一些例子。
第一个,非常简单的,使用add求和:
1
2
3
|
>>>
from
pipe
import
*
>>>
range
(
5
) | add
10
|
求偶数和需要使用到where,作用类似于内建函数filter,过滤出符合条件的元素:
1
2
|
>>>
range
(
5
) | where(
lambda
x: x
%
2
=
=
0
) | add
6
|
还记得我们定义的斐波那契数列生成器吗?求出数列中所有小于10000的偶数和需要用到take_while,与itertools的同名函数有类似的功能,截取元素直到条件不成立:
1
2
3
4
5
|
>>> fib
=
fibonacci
>>> fib() | where(
lambda
x: x
%
2
=
=
0
)\
... | take_while(
lambda
x: x <
10000
)\
... | add
3382
|
需要对元素应用某个函数可以使用select,作用类似于内建函数map;需要得到一个列表,可以使用as_list:
1
2
|
>>> fib() | select(
lambda
x: x
*
*
2
) | take_while(
lambda
x: x <
100
) | as_list
[
1
,
1
,
4
,
9
,
25
,
64
]
|
pipe中还包括了更多的流处理函数。你甚至可以自己定义流处理函数,只需要定义一个生成器函数并加上修饰器Pipe。如下定义了一个获取元素直到索引不符合条件的流处理函数:
1
2
3
4
5
6
|
>>> @Pipe
...
def
take_while_idx(iterable, predicate):
...
for
idx, x
in
enumerate
(iterable):
...
if
predicate(idx):
yield
x
...
else
:
return
...
|
使用这个流处理函数获取fib的前10个数字:
1
2
|
>>> fib() | take_while_idx(
lambda
x: x <
10
) | as_list
[
1
,
1
,
2
,
3
,
5
,
8
,
13
,
21
,
34
,
55
]
|
更多的函数就不在这里介绍了,你可以查看pipe的源文件,总共600行不到的文件其中有300行是文档,文档中包含了大量的示例。
pipe实现起来非常简单,使用Pipe装饰器,将普通的生成器函数(或者返回迭代器的函数)代理在一个实现了__ror__方法的普通类实例上即可,但是这种思路真的很有趣。