Python中列表解析式与生成器表达式
列表解析式List Comprehension与生成器表达式 Generator expression
列表解析式
语法
- [返回值 for 元素 in 可迭代对象 if 条件]
- 使用[ ],内部是for循环,,if条件语句可选
- 返回一个新的列表
列表解析式是一种语法糖
- 编译器会优化,不会因为简写而影响效率,反而因优化提高了效率
- 减少程序员工作量,减少出错
- 简化了代码,但可读性增强
例: 获取10以为的偶数
# 非列表解析式写法
even =[]
for x in rang(10):
if x % 2 == 0:
even.append(x)
------------------------------
# 列表解析式写法
even = [x for x in range(10) if x%2 == 0]
以下例子返回值会是什么?
newlist = [print(i) for i in range(10)]
print(newlist)
newlist1 = [str(i) for i in range(10)]
print(newlist1)
输出结果:
0
1
2
3
4
5
6
7
8
9
[None, None, None, None, None, None, None, None, None, None]
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
- 解析:newlist最后列表中会出现10个None,是因为必须要把函数执行完了用print(i)的返回值来填充列表,而print(i)的返回值是None。newlist1中str(i)的返回值是字符串
列表解析进阶
- [expr for item in iterable if cond1 if cond2],不允许出现else
等价于
ret = [ ]
for iterm in iterable:
if cond1:
if cond2:
ret.append(expr) - 例: 20以内,既能被2整除又能被3整除的数
print([i for i in range(20) if i%2 == 0 and i%3 == 0])
print([i for i in range(20) if i%2 == 0 if i%3 == 0])
输出结果
[0, 6, 12, 18]
[0, 6, 12, 18]
- [expr for i in iterable1 for j in iterable2 ]
等价于
ret = []
for i in iterable1:
for j in iterable2:
ret.append(expr) - 例:
print([(x,y) for x in 'abc' for y in range(3)])
print([[x,y] for x in 'abc' for y in range(3)])
print([{x,y} for x in 'abc' for y in range(3)])
输出结果:
[('a', 0), ('a', 1), ('a', 2), ('b', 0), ('b', 1), ('b', 2), ('c', 0), ('c', 1), ('c', 2)]
[['a', 0], ['a', 1], ['a', 2], ['b', 0], ['b', 1], ['b', 2], ['c', 0], ['c', 1], ['c', 2]]
[{0, 'a'}, {1, 'a'}, {2, 'a'}, {0, 'b'}, {1, 'b'}, {2, 'b'}, {0, 'c'}, {1, 'c'}, {2, 'c'}]
请问下面3中输出各是什么?
print([(x,y) for x in range(7) if x >4 for y in range(20,25) if y>23])
print([(x,y) for x in range(7) for y in range(20,25)if x>4 if y>23])
print([(x,y) for x in range(7) for y in range(20,25)if x>4 and y>23])
输出结果
[(5, 24), (6, 24)]
[(5, 24), (6, 24)]
[(5, 24), (6, 24)]
生成器表达式
语法
- (返回值 for 元素 in 可迭代对象 if 条件)
- 将列表解析式的 [] 换成 () 即可。
- 使用(),内部是for循环,,if条件语句可选
- 返回一个生成器
和列表解析式的区别
- 生成器表达式是按需要计算(或称惰性求值 、延迟计算),需要的时候才计算值
- 列表解析式是立即返回值
生成器
- 可迭代对象
- 迭代器
- 例1使用生成器表达式:
g =("{:04}".format(i) for i in range(1,5))
next(g)
for x in g:
print(x)
print('-----------')
for x in g:
print(x)
输出结果
0002
0003
0004
-----------
- 解析:1.延迟计算;2.返回迭代器,可以迭代;3.从前到后走完一遍后,不能回头
- 例2:将例1改成列表解析式:
g =["{:04}".format(i) for i in range(1,5)]
for x in g:
print(x)
print('-----------')
for x in g:
print(x)
输出结果
0001
0002
0003
0004
-----------
0001
0002
0003
0004
- 解析:1.立即计算;2.返回的不是迭代,返回可迭代对象列表;3.从前到后走完一遍,可以重新回头迭代
生成器和列表解析式对比
计算方法
- 生成器表达式延时计算,列表解析式立即计算
内存占用
- 单从返回值本身来说,生成器表达式省内存,列表解析式返回新的列表
- 生成器没有数据,内存占用极少,它是使用时一个个返回数据。如果将这些返回的数据合起来占用的内存和列表解析式差不多。但是它不需要立即占用这么多内存
- 列表解析式构造性的列表需要立即占用内存,不管你是否需要立即使用这么多数据
计算速度
- 单从计算时间看,生成器表达式耗时非常短,列表解析式耗时长
- 但是生成器本身并没有返回任何值,只返回了一个生成器对象
- 列表解析式构造并返回了一个新的列表,所以看起来耗时了
集合解析式
语法
- {返回值for 元素 in可迭代对象 if 条件}
- 列表解析式的[]换成{}即可
- 立即返回一个集合
用法
{(x, x+1) for x in range(10)}
注: {[x] for x in range(10)},会报TypeError错误,因为[x]是list,集合内的元素需要可hash,而list是不可hash
字典解析式
语法
- {返回值for 元素 in可迭代对象 if 条件}
- 列表解析式的[]换成{}即可
- 使用key.value形式
- key值需要可hash
- 立即返回一个字典
用法
{x:(x,x+1) for x in range(5)}
{x:[x,x+1] for x in range(5)}
{(x,):[x,x+1] for x in range(5)}
注:{[x]:[x,x+1] for x in range(5)},会报TypeError错误,因为key是list,不可hash
{str(x):y for x in range(3) for y in range(4)} 会输出几个元素呢?
输出结果: {‘0’:3, ‘1’:3, ‘2’:3} 原因是字典有去重特性,而后一次的输出结果会覆盖前一次。
总结
- Python2 引入列表解析式
- Python2.4 引入生成器表达式
- Python3 引入集合、字典解析式,并迁徙到了2.7
- 一般来说,应该多应用解析式,简短、高效
- 如果一个解析式非常复杂,难以读懂,可以考虑拆解成for 循环
- 生成器和迭代是不同的对象,但都是可迭代对象
- 可迭代对象范围更大,都可以使用for循环遍历
习题:
第一题 使用列表解析式 打印九九乘法表 第一题
第二题 使用列表解析式生成 “0001.abadicddws” 格式的ID号 第一题
要求ID格式是以点号分割,左边是4位从1开始的整数,右边是10位随机小写英文字母。
请依次生成前100个ID的列表,如图:
'0001.bbcomlkdyt'
'0002.yzjudytpna'
'0003.zycscjsvfy'
'0004.hhvqzlukui'
'0005.xissuxwsou'