浅谈python 代码生成器中列表生成式和生成器的区别

python 中的列表解析和生成表达式
python 中的列表解析和生成表达式
列表解析 在需要改变列表而不是需要新建某列表时,可以使用列表解析。列表解析表达式为: [expr for iter_var in iterable] [expr for iter_var in iterable if cond_expr] 第一种语法:首先迭代iterable里所有内容,每一次迭代,都把iterable里相应内容放到iter_var中,再在表达式中应用该iter_var的内容,最后用表达式的计算值生成一个列表。 第二种语法:加入了判断语句,只有满足条件的内容才把iterable里相应内容放到iter_var中,再在表达式中应用该iter_var的内容,最后用表达式的计算值生成一个列表。 举例如下:   复制代码 代码如下:  &&& L= [(x+1,y+1) for x in range(3) for y in range(5)] &&& L [(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)] &&& N=[x+10 for x in range(10) if x&5] &&& N [16, 17, 18, 19]
生成器表达式 生成器表达式是在python2.4中引入的,当序列过长, 而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析。生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[],如下:   复制代码 代码如下:  (expr for iter_var in iterable) (expr for iter_var in iterable if cond_expr)
例:   复制代码 代码如下:  &&& L= (i + 1 for i in range(10) if i % 2) &&& L &generator object &genexpr& at 0xb749a52c& &&& L1=[] &&& for i in L: ... L1.append(i) ... &&& L1 [2, 4, 6, 8, 10]
生成器表达式并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来。 生成器表达式使用了“惰性计算”(lazy evaluation,也有翻译为“延迟求值”,我以为这种按需调用call by need的方式翻译为惰性更好一些),只有在检索时才被赋值( evaluated),所以在列表比较长的情况下使用内存上更有效。A generator object in python is something like a lazy list. The elements are only evaluated as soon as you iterate over them. 一些说明: 1. 当需要只是执行一个循环的时候尽量使用循环而不是列表解析,这样更符合python提倡的直观性。   复制代码 代码如下:  for item in sequence: process(item)
2. 当有内建的操作或者类型能够以更直接的方式实现的,不要使用列表解析。 例如复制一个列表时,使用:L1=list(L)即可,不必使用:   复制代码 代码如下:  L1=[x for x in L]
3. 如果需要对每个元素都调用并且返回结果时,应使用L1=map(f,L), 而不是 L1=[f(x) for x in L]
H3C认证Java认证Oracle认证
基础英语软考英语项目管理英语职场英语
.NETPowerBuilderWeb开发游戏开发Perl
二级模拟试题一级模拟试题一级考试经验四级考试资料
港口与航道工程建设工程法规及相关知识建设工程经济考试大纲矿业工程市政公用工程通信与广电工程
操作系统汇编语言计算机系统结构人工智能数据库系统微机与接口
软件测试软件外包系统分析与建模敏捷开发
法律法规历年试题软考英语网络管理员系统架构设计师信息系统监理师
高级通信工程师考试大纲设备环境综合能力
路由技术网络存储无线网络网络设备
CPMP考试prince2认证项目范围管理项目配置管理项目管理案例项目经理项目干系人管理
Powerpoint教程WPS教程
电子政务客户关系管理首席信息官办公自动化大数据
职称考试题目
就业指导签约违约职业测评
招生信息考研政治
网络安全安全设置工具使用手机安全
3DMax教程Flash教程CorelDraw教程Director教程
Dreamwaver教程HTML教程网站策划网站运营Frontpage教程
生物识别传感器物联网传输层物联网前沿技术物联网案例分析
互联网电信IT业界IT生活
Java核心技术J2ME教程
Linux系统管理Linux编程Linux安全AIX教程
Windows系统管理Windows教程Windows网络管理Windows故障
组织运营财务资本
视频播放文件压缩杀毒软件输入法微博
数据库开发Sybase数据库Informix数据库
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&python(四)上:列表生成式、生成器、迭代器和内置函数-中国学网-中国IT综合门户网站-提供健康,养生,留学,移民,创业,汽车等信息
python(四)上:列表生成式、生成器、迭代器和内置函数
来源:互联网 更新时间: 17:09:46 责任编辑:李志喜字体:
列表生成式
一、列表生成式
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11)):
&&& list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
但如果要生成[1x1, 2x2, 3x3, &, 10x10]怎么做?方法一是循环:
&&& L = []
&&& for x in range(1, 11):
L.append(x * x)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list:
&&& [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
还可以使用两层循环,可以生成全排列:
&&& [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value:
&&& d = {'x': 'A', 'y': 'B', 'z': 'C' }
&&& for k, v in d.items():
print(k, '=', v)
因此,列表生成式也可以使用两个变量来生成list:
&&& d = {'x': 'A', 'y': 'B', 'z': 'C' }
&&& [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
把一个list中所有的字符串变成小写:
&&& L = ['Hello', 'World', 'IBM', 'Apple']
&&& [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
相关文章:
<a href="/cse/search?q=<inputclass="s-btn"type="submit"text="<inputclass="s-btn"type="submit"text="<buttonhidefocusclass="s-btnjs-ask-btn"text="我要提问
<a href="/cse/search?q=Python 自学开始(二,基础)
可以看出,Python的for循环抽象程度要高于Java的for循环,因为Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。list这种数据类型虽然有下标,但很多其他数据类型是没有下标的
高级特性:
取一个list或tuple的部分元素是非常常见的操作。比如,一个list如下:
&&& L = [&#39;Michael&#39;, &#39;Sarah&#39;, &#39;Tracy&#39;, &#39;Bob&#39;, &#39;Jack&#39;]
提供了切片(Slice)操作符,能大大简化这种操作。
对应上面的问题,取前3个元素,用一行代码就可以完成切片:
&&& L[0:3]
[&#39;Michael&#39;, &#39;Sarah&#39;, &#39;Tracy&#39;]
L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。如果第一个索引是0,还可以省略:
可以通过切片轻松取出某一段数列。比如前10个数:
&&& L[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
后10个数:
&&& L[-10:]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
前10个数,每两个取一个:
&&& L[:10:2]
[0, 2, 4, 6, 8]
所有数,每5个取一个:
&&& L[::5]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,65, 70, 75, 80, 85, 90, 95]
甚至什么都不写,只写[:]就可以原样复制一个list:
[0, 1, 2, 3, ..., 99]
tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:
&&& (0, 1, 2, 3, 4, 5)[:3]
字符串&xxx&或Unicode字符串u&xxx&也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:
&&& &#39;ABCDEFG&#39;[:3]
&#39;ABC&#39;
&&& &#39;ABCDEFG&#39;[::2]
在Python中,迭代是通过for … in来完成的,而很多语言比如C或者,迭代list是通过下标完成的,比如Java代码
for (i=0; i&list. n="list[i];" pre=""&
可以看出,Python的for循环抽象程度要高于Java的for循环,因为Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。list这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可迭代对象,无论有无下标,都可以迭代,比如dict就可以迭代:>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
b因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.itervalues(),如果要同时迭代key和value,可以用for k, v in d.iteritems()。由于字符串也是可迭代对象,因此,也可以作用于for循环:>>> for ch in 'ABC':
C所以,当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
>>> isinstance([1,2,3], Iterable) # list是否可迭代
>>> isinstance(123, Iterable) # 整数是否可迭代
False三、列表生成:
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用range(1, 11):>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]但如果要生成[1x1, 2x2, 3x3, …, 10x10]怎么做?方法一是循环:>>> L = []
>>> for x in range(1, 11):
L.append(x * x)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list:>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来,十分有用,多写几次,很快就可以熟悉这种语法。for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]还可以使用两层循环,可以生成全排列:>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']四、生成器(不太懂,后面补充)五、高阶函数
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
一个最简单的高阶函数:def add(x, y, f):
return f(x) + f(y)当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:x ==> -5
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11编写高阶函数,就是让函数的参数能够接收别的函数。
①map/reduce
Python内建了map()和reduce()函数。
如果你读过Google的那篇大名鼎鼎的论文“MapReduce: Simplified Data Processing on Large Clusters”,你就能大概明白map/reduce的概念。我们先看map。map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。
举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:
>>> def f(x):
return x * x
>>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]map()传入的第一个参数是f,即函数对象本身。你可能会想,不需要map()函数,写一个循环,也可以计算出结果:L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
L.append(f(n))
print L的确可以,但是,从上面的循环代码,能一眼看明白“把f(x)作用在list的每一个元素并把结果生成一个新的list”吗?
所以,map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:>>> map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])
['1', '2', '3', '4', '5', '6', '7', '8', '9']只需要一行代码。再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)比方说对一个序列求和,就可以用reduce实现:>>> def add(x, y):
return x + y
>>> reduce(add, [1, 3, 5, 7, 9])
25当然求和运算可以直接用Python内建函数sum(),没必要动用reduce。但是如果要把序列[1, 3, 5, 7, 9]变换成整数13579,reduce就可以派上用场:>>> def fn(x, y):
return x * 10 + y
>>> reduce(fn, [1, 3, 5, 7, 9])
13579这个例子本身没多大用处,但是,如果考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数:>>> def fn(x, y):
return x * 10 + y
>>> def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
>>> reduce(fn, map(char2num, '13579'))
13579整理成一个str2int的函数就是:def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
return reduce(fn, map(char2num, s))②filter
Python内建的filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:def is_odd(n):
return n % 2 == 1
filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])
# 结果: [1, 5, 9, 15]③sorted
排序算法,排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。通常规定,对于两个元素x和y,如果认为x
y,则返回1,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。Python内置的sorted()函数就可以对list进行排序:>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]六、返回函数函数作为返回值高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:def calc_sum(*args):
for n in args:
ax = ax + n
return ax在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:>>> f1()
9返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。七、匿名函数
当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。
在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
八、装饰器
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
一个完整的decorator的写法如下:import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper者针对带参数的decorator:import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decoratorimport functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator九、偏函数
Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。要注意,这里的偏函数和数学意义上的偏函数不一样。在介绍函数参数的时候,我们讲到,通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。举例如下
但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:>>> int('12345', base=8)
>>> int('12345', 16)
74565假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:def int2(x, base=2):
return int(x, base)functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
>>> int2('1010101')
85所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。类似xxx这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的author,name就是特殊变量,hello模块定义的文档注释也可以用特殊变量doc访问,我们自己的变量一般不要用这种变量名;
类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;Python函数式编程指南(4):生成器 - Python - 伯乐在线
& Python函数式编程指南(4):生成器
4. 生成器(generator)
4.1. 生成器简介
首先请确信,生成器就是一种迭代器。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。另外,对于生成器的特殊语法支持使得编写一个生成器比自定义一个常规的迭代器要简单不少,所以生成器也是最常用到的特性之一。
从Python 2.5开始,[PEP 342:通过增强生成器实现协同程序]的实现为生成器加入了更多的特性,这意味着生成器还可以完成更多的工作。这部分我们会在稍后的部分介绍。
4.2. 生成器函数
4.2.1. 使用生成器函数定义生成器
如何获取一个生成器?首先来看一小段代码:
&&& def get_0_1_2():
... yield 0
... yield 1
... yield 2
&&& get_0_1_2
&function get_0_1_2 at 0x00B2CB70&
&&& 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成为了一个生成器函数。生成器函数的特性如下:
调用生成器函数将返回一个生成器;
&&& generator = get_0_1_2()
&&& generator
&generator object get_0_1_2 at 0x00B1C7D8&
&&& generator = get_0_1_2()&&& generator&generator object get_0_1_2 at 0x00B1C7D8&
第一次调用生成器的next方法时,生成器才开始执行生成器函数(而不是构建生成器时),直到遇到yield时暂停执行(挂起),并且yield的参数将作为此次next方法的返回值;
&&& generator.next()
&&& generator.next()0
之后每次调用生成器的next方法,生成器将从上次暂停执行的位置恢复执行生成器函数,直到再次遇到yield时暂停,并且同样的,yield的参数将作为next方法的返回值;
&&& generator.next()
&&& generator.next()
&&& generator.next()1&&& generator.next()2
如果当调用next方法时生成器函数结束(遇到空的return语句或是到达函数体末尾),则这次next方法的调用将抛出StopIteration异常(即for循环的终止条件);
&&& generator.next()
Traceback (most recent call last):
File "&stdin&", line 1, in &module&
StopIteration
&&& generator.next()Traceback (most recent call last):&&File "&stdin&", line 1, in &module&StopIteration
生成器函数在每次暂停执行时,函数体内的所有变量都将被封存(freeze)在生成器中,并将在恢复执行时还原,并且类似于闭包,即使是同一个生成器函数返回的生成器,封存的变量也是互相独立的。
我们的小例子中并没有用到变量,所以这里另外定义一个生成器来展示这个特点:
&&& def fibonacci():
while True:
a, b = b, a+b
&&& for num in fibonacci():
if num & 100: break
print num,
1 1 2 3 5 8 13 21 34 55 89
12345678910111213
&&& 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
看到while True可别太吃惊,因为生成器可以挂起,所以是延迟计算的,无限循环并没有关系。这个例子中我们定义了一个生成器用于获取斐波那契数列。
4.2.2. 生成器函数的FAQ
接下来我们来讨论一些关于生成器的有意思的话题。
1. 你的例子里生成器函数都没有参数,那么生成器函数可以带参数吗?
当然可以啊亲,而且它支持函数的所有参数形式。要知道生成器函数也是函数的一种:)
&&& def counter(start=0):
while True:
yield start
start += 1
&&& def counter(start=0):...&& while True:...&&&& yield start...&&&& start += 1...
这是一个从指定数开始的计数器。
2. 既然生成器函数也是函数,那么它可以使用return输出返回值吗?
不行的亲,是这样的,生成器函数已经有默认的返回值——生成器了,你不能再另外给一个返回值;对,即使是return None也不行。但是它可以使用空的return语句结束。如果你坚持要为它指定返回值,那么Python将在定义的位置赠送一个语法错误异常,就像这样:
&&& def i_wanna_return():
yield None
return None
File "&stdin&", line 3
SyntaxError: 'return' with argument inside generator
&&& def i_wanna_return():...&& yield None...&& return None...&&File "&stdin&", line 3SyntaxError: 'return' with argument inside generator
3. 好吧,那人家需要确保释放资源,需要在try&#8230;finally中yield,这会是神马情况?(我就是想玩你)我在finally中还yield了一次!
Python会在真正离开try&#8230;finally时再执行finally中的代码,而这里遗憾地告诉你,暂停不算哦!所以结局你也能猜到吧!
&&& def play_u():
&&& for val in play_u(): print val,
1234567891011
&&& def play_u():...&& try:...&&&& yield 1...&&&& yield 2...&&&& yield 3...&& finally:...&&&& yield 0...&&& for val in play_u(): print val,...1 2 3 0
*这与return的情况不同。return是真正的离开代码块,所以会在return时立刻执行finally子句。
*另外,“在带有finally子句的try块中yield”定义在PEP 342中,这意味着只有Python 2.5以上版本才支持这个语法,在Python 2.4以下版本中会得到语法错误异常。
4. 如果我需要在生成器的迭代过程中接入另一个生成器的迭代怎么办?写成下面这样好傻好天真。。
&&& def sub_generator():
for val in counter(10): yield val
&&& def sub_generator():...&& yield 1...&& yield 2...&& for val in counter(10): yield val...
这种情况的语法改进已经被定义在[PEP 380:委托至子生成器的语法]中,据说会在Python 3.3中实现,届时也可能回馈到2.x中。实现后,就可以这么写了:
&&& def sub_generator():
yield from counter(10)
File "&stdin&", line 4
yield from counter(10)
SyntaxError: invalid syntax
&&& def sub_generator():...&& yield 1...&& yield 2...&& yield from counter(10)&&File "&stdin&", line 4&&&&yield from counter(10)&&&&&&&&&&&& ^SyntaxError: invalid syntax
看到语法错误木有?现在我们还是天真一点吧~
有更多问题?请回复此文:)
4.3. 协同程序(coroutine)
协同程序(协程)一般来说是指这样的函数:
彼此间有不同的局部变量、指令指针,但仍共享全局变量;
可以方便地挂起、恢复,并且有多个入口点和出口点;
多个协同程序间表现为协作运行,如A的运行过程中需要B的结果才能继续执行。
协程的特点决定了同一时刻只能有一个协同程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁、或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。
从另一方面说,协程无法并发其实也将它的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多的被拿来与常规函数进行比较,而不是与线程。当然,线程比协程复杂许多,功能也更强大,所以我建议大家牢牢地掌握线程即可:Python线程指南
这一节里我也就不列举关于协程的例子了,以下介绍的方法了解即可。
Python 2.5对生成器的增强实现了协程的其他特点,在这个版本中,生成器加入了如下方法:
1. send(value):
send是除next外另一个恢复生成器的方法。Python 2.5中,yield语句变成了yield表达式,这意味着yield现在可以有一个值,而这个值就是在生成器的send方法被调用从而恢复执行时,调用send方法的参数。
&&& def repeater():
while True:
n = (yield n)
&&& r = repeater()
&&& r.next()
&&& r.send(10)
12345678910
&&& def repeater():...&& n = 0...&& while True:...&&&& n = (yield n)...&&& r = repeater()&&& r.next()0&&& r.send(10)10
*调用send传入非None值前,生成器必须处于挂起状态,否则将抛出异常。不过,未启动的生成器仍可以使用None作为参数调用send。
*如果使用next恢复生成器,yield表达式的值将是None。
2. close():
这个方法用于关闭生成器。对关闭的生成器后再次调用next或send将抛出StopIteration异常。
3. throw(type, value=None, traceback=None):
这个方法用于在生成器内部(生成器的当前挂起处,或未启动时在定义处)抛出一个异常。
*别为没见到协程的例子遗憾,协程最常见的用处其实就是生成器。
4.4. 一个有趣的库:pipe
这一节里我要向诸位简要介绍pipe。pipe并不是Python内置的库,如果你安装了easy_install,直接可以安装它,否则你需要自己下载它:http://pypi.python.org/pypi/pipe
之所以要介绍这个库,是因为它向我们展示了一种很有新意的使用迭代器和生成器的方式:流。pipe将可迭代的数据看成是流,类似于linux,pipe使用&#8217;|&#8217;传递数据流,并且定义了一系列的“流处理”函数用于接受并处理数据流,并最终再次输出数据流或者是将数据流归纳得到一个结果。我们来看一些例子。
第一个,非常简单的,使用add求和:
&&& from pipe import *
&&& range(5) | add
&&& from pipe import *&&& range(5) | add10
求偶数和需要使用到where,作用类似于内建函数filter,过滤出符合条件的元素:
&&& range(5) | where(lambda x: x % 2 == 0) | add
&&& range(5) | where(lambda x: x % 2 == 0) | add6
还记得我们定义的斐波那契数列生成器吗?求出数列中所有小于10000的偶数和需要用到take_while,与itertools的同名函数有类似的功能,截取元素直到条件不成立:
&&& fib = fibonacci
&&& fib() | where(lambda x: x % 2 == 0)\
| take_while(lambda x: x & 10000)\
&&& fib = fibonacci&&& fib() | where(lambda x: x % 2 == 0)\...&&&&&& | take_while(lambda x: x & 10000)\...&&&&&& | add3382
需要对元素应用某个函数可以使用select,作用类似于内建函数map;需要得到一个列表,可以使用as_list:
&&& fib() | select(lambda x: x ** 2) | take_while(lambda x: x & 100) | as_list
[1, 1, 4, 9, 25, 64]
&&& fib() | select(lambda x: x ** 2) | take_while(lambda x: x & 100) | as_list[1, 1, 4, 9, 25, 64]
pipe中还包括了更多的流处理函数。你甚至可以自己定义流处理函数,只需要定义一个生成器函数并加上修饰器Pipe。如下定义了一个获取元素直到索引不符合条件的流处理函数:
... def take_while_idx(iterable, predicate):
for idx, x in enumerate(iterable):
if predicate(idx): yield x
else: return
&&& @Pipe... def take_while_idx(iterable, predicate):...&& for idx, x in enumerate(iterable):...&&&& if predicate(idx): yield x...&&&& else: return...
使用这个流处理函数获取fib的前10个数字:
&&& fib() | take_while_idx(lambda x: x & 10) | as_list
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
&&& 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__方法的普通类实例上即可,但是这种思路真的很有趣。
函数式编程指南全文到这里就全部结束了,希望这一系列文章能给你带来帮助。希望大家都能看到一些结构式编程之外的编程方式,并且能够熟练地在恰当的地方使用 :)
明天我会整理一个目录放上来方便查看,并且列出一些供参考的文章。遗憾的是这些文章几乎都是英文的,请努力学习英语吧 &#8211; -#
可能感兴趣的话题
关于 Python 频道
Python频道分享 Python 开发技术、相关的行业动态。
新浪微博:
推荐微信号
(加好友请注明来意)
&#8211; 好的话题、有启发的回复、值得信赖的圈子
&#8211; 分享和发现有价值的内容与观点
&#8211; 为IT单身男女服务的征婚传播平台
&#8211; 优秀的工具资源导航
&#8211; 翻译传播优秀的外文文章
&#8211; 国内外的精选文章
&#8211; UI,网页,交互和用户体验
&#8211; 专注iOS技术分享
&#8211; 专注Android技术分享
&#8211; JavaScript, HTML5, CSS
&#8211; 专注Java技术分享
&#8211; 专注Python技术分享
& 2016 伯乐在线

我要回帖

更多关于 python 生成器 迭代器 的文章

 

随机推荐