列表解析与map
map把一个函数映射遍一个序列;列表把一个表达式映射到一个序列:
>>> list(map(ord, 'spam'))
[115, 112, 97, 109]
>>> [ord(x) for x in 'spam']
[115, 112, 97, 109]
增加测试和嵌套循环:
>>> [x for x in range(5) if x % 2 == 0]
[0, 2, 4]
>>> list(filter((lambda x: x%2 == 0), range(5)))
[0, 2, 4]
>>> list(map((lambda x: x**2), filter((lambda x: x%2 == 0), range(10))))
[0, 4, 16, 36, 64]
>>>
你可以在一个列表解析中编写任意数量的嵌套的for循环,并且每一个都有可选的if测试:
[expression for target1 in iterable1 [if condition]
for target2 in iterable2 [if condition]
... ...
for targetN in iterableN [if condition]
>>> [x+y for x in [1,2,3] for y in [100,200,300]]
[101, 201, 301, 102, 202, 302, 103, 203, 303]
>>> [(x,y) for x in range(5) if x%2 == 0 for y in range(5) if y%2 == 1]
[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
列表解析和矩阵
Python编写矩阵(多维数组)的一个最基本的方法计算使用嵌套的列表结构:
>>> m1 = [[1,2,3],
[4,5,6],
[7,8,9]]
>>> m1[1]
[4, 5, 6]
>>> [row[1] for row in m1]
[2, 5, 8]
当前python的测试下,map调用比等效的for循环要快两倍,而列表解析往往比mao调用要稍快一些。速度差距来自底层实现,map和列表解析在解释器中以C语言的速度运行的,比python的for循环在PVM中运行要快得多。
实际应用,用列表解析和map去除文件换行:
>>> open('threenames.py').readlines()
["a = 'dude'\n", "b = 'parrot'\n", "c = 'sketch'\n", 'print(a,b,c)\n']
>>> [line.rstrip() for line in open('threenames.py').readlines()]
["a = 'dude'", "b = 'parrot'", "c = 'sketch'", 'print(a,b,c)']
>>> [line.rstrip() for line in open('threenames.py')]
["a = 'dude'", "b = 'parrot'", "c = 'sketch'", 'print(a,b,c)']
>>> list(map((lambda x: x.rstrip()), open('threenames.py')))
["a = 'dude'", "b = 'parrot'", "c = 'sketch'", 'print(a,b,c)']
>>>
列表解析还能作为一种列选择操作来使用。python的标准SQL数据库API将返回查询结果保存为与下班类似的元组列表:列表就是表,而元组是行,元组中的元素就是列的值:
>>> listOfTuple = [('bob', 35, 'mgr'), ('mel', 40, 'dev')]
>>> [age for (name, age, job) in listOfTuple]
[35, 40]
>>> list(map((lambda row: row[1]), listOfTuple))
[35, 40]
>>>
重访迭代器:生成器
生成器函数:编写为常规的def语句,都是使用yield语句一次返回一个结果,在每个结果之间挂起和继续它们的状态。
生成器表达式:类似于上一小节的列表解析,但是,他们返回按需产生结果的一个对象,而不是构建一个结果列表。
二者都不会一次性构建一个列表,节省了内存空间,并且允许计算时间分散到各个结果请求。
生成器函数: yield VS return
状态挂起:生成器函数和常规函数之间的主要的代码不同之处在于,生成器yield一个值,而不是返回一个值。yield语句挂起该函数并向调用者发送回一个值,但是,保留足够的状态使得函数能够从它离开的地方继续。当继续时,函数在上一个yield返回后立即继续执行。
迭代协议整合:
可迭代对象定义了一个__next__方法,它要么返回迭代中的下一项,或者引发一个特殊的StopIteration异常来终止迭代。
生成器函数应用:
>>> def gensquares(N):
for i in range(N):
yield i**2
>>> a = gensquares(6) # 得到一个生成器对象,它支持迭代协议
>>> next(a)
0
>>> next(a)
1
>>> next(a)
4
>>> next(a)
9
>>> next(a)
16
>>> next(a)
25
>>> next(a) # raise一个StopIteration
Traceback (most recent call last):
File "<pyshell#123>", line 1, in <module>
next(a)
StopIteration
>>>
生成器表达式:迭代器遇到列表解析
生成器表达式就像一般的列表解析一样,但是他们是括 圆括号中而不是方括号中的。
>>> [x**2 for x in range(5)] # list comprehension
[0, 1, 4, 9, 16]
>>> (x**2 for x in range(5)) # generator expression
<generator object <genexpr> at 0x000002993B3E7E60>
编写一个列表解析基本上等同于:在一个list内置调用中包含一个生成器表达式以迫使其一次生成列表中所有的结果。
>>> list(x**2 for x in range(5))
[0, 1, 4, 9, 16]
我们一般不会机械的使用next迭代器来操作生成器表达式,因为for循环会自动触发。
>>> for num in (x**2 for x in range(5)):
print(num)
0
1
4
9
16
如果生成器表达式是在嵌套的括号之内,就像在那些函数调用中,生成器自身的括号就不是必须的了。如下,第二个sorted例外
>>> sum(x**2 for x in range(5))
30
>>> sorted(x **2 for x in range(5))
[0, 1, 4, 9, 16]
>>> sorted(x ** 2 for x in range(5), reverse=True)
SyntaxError: Generator expression must be parenthesized if not sole argument
>>> sorted((x ** 2 for x in range(5)), reverse=True)
[16, 9, 4, 1, 0]
>>> import math
>>> list(map(math.sqrt, (x**2 for x in range(5))))
[0.0, 1.0, 2.0, 3.0, 4.0]
>>>
生成器表达式大体上可以认为是对内存空间的优化,它们不需要像方括号的列表解析一样,一次构造出整个结果列表。它们在实际中运行起来可能稍慢一些,所以它们可能只对于非常大的结果集合的运算来说是最优的选择。
生成器是单迭代器对象
生成器函数和生成器表达式自身都是迭代器,并由此只支持一次活跃迭代--不像一些内置类型。
>>> g = (c * 4 for c in 'spam')
>>> g1 = iter(g)
>>> next(g1)
'ssss'
>>> next(g1)
'pppp'
>>> g2 = iter(g) # 手动使用迭代会指向相同的位置
>>> next(g2)
'aaaa'
>>> g is g1
True
>>> g is g2
True
>>> g1 is g2
True
>>> ll = [1,2,3,4]
>>> l1, l2 = iter(ll), iter(ll) # 这个不是一次性活跃迭代,指向不同位置
>>> l1 is l2
False
>>>
除了列表解析和生成器, python3中可用的解析表达式活跃集合解析和字典解析。
集合: {1,3,2}等同于set([1,3,2]),而新的集合解析语法{f(x) for x in S if P(x)} 就像是生成器表达式set(f(x) for x in S if P(x))
字典:新的字典解析语法{key: val for (key, val) in zip(keys, vals)}像dict(zip(keys, vals))一样工作,并且{x :f(x) for x in items}就像生成器表达式dict((x, f(x)) for x in items)一样。
>>> [x * x for x in range(5)] # list comprehension
[0, 1, 4, 9, 16]
>>> (x * x for x in range(5)) # generator expression
<generator object <genexpr> at 0x000002993B3E7F68>
>>> {x * x for x in range(5)} # set comprehension
{0, 1, 4, 9, 16}
>>> {x: x * x for x in range(5)} # dictionary comprehension
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>>
Learning Python, Fourth Edition, by Mark Lutz.