从一个问题来看解析式,现有如下需求:生成一个列表,元素0-9,对每一个元素自增1后求平方返回新列表。
lst = list(range(10))
lst2 = []
for value in lst:
lst2.append((value + 1) ** 2)
print(lst2)
看起来很容易理解,但是这种需求竟然用了5行代码!下面来看一下列表解析式的写法。
[ (x+1)**2 for x in range(10)]
看起来非常简洁,属于Python的风格!哈哈
再来看一下,什么是列表解析式?在Python中列表解析式是一种语法糖,虽然对看似复杂的代码进行了简写,但是编译器会进行优化,不会因为简写而影响效率,反而因为优化提高了效率。另外还介绍了代码量,减少了出错的机会,还简化了代码,增加了代码可读性。
列表解析式的基本语法是如下
[ 返回值 for 元素 in 可迭代对象 if 条件]
中括号
将表达式(推导式)括起来elif语句
有这样的赋值语句 newlist = [ print(i) for i in range(10) ]
,请问newlist打印出来是什么?
In [6]: newlist = [ print(i) for i in range(10) ]
0
1
2
3
4
5
6
7
8
9
In [7]: newlist
Out[7]: [None, None, None, None, None, None, None, None, None, None]
为什么是None?因为表达式只会将函数的返回值作为结果,进行添加,所以当返回值是一个函数操作的对象时,一定要注意函数的返回值!
有的时候我们的代码需要进行两个或多个循环,列表解析式进阶版本可以满足这种需求哦。它的语法是:
[ 返回值 for 元素 in 可迭代对象 if 条件表达式1 if 条件表达式2 ... ]
等同于:
for 元素 in 可迭代对象:
if 条件表达式1:
if 条件表达式2:
返回值
[ 返回值 for 元素 in 可迭代对象1 for 元素 in 可迭代对象2 ... ]
等同于:
for 元素1 in 可迭代对象1:
for 元素2 in 可迭代对象2:
# if 也可以加条件判断
返回值[1个或多个]
例子:
20以内,既能被2整除,又能被3整除的列表
[ i for i in range(20) if i % 2 == 0 if i % 3 == 0]
[(i,j) for i in range(10) for j in range(20) if i < 3 if j > 18]
表示当i小于3时,j大于18时,组成一个元素返回
除了列表解析式以外,Python中还存在集合解析式
、字典解析式
、'元组解析式'
。
可不是什么元组解析式,这行小字你看不到,可不怪我哦。
{ 返回值 for 元素 in 可迭代对象 if 条件 }
{}
即可20以内,既能被2整除,又能被3整除的集合
{ i for i in range(20) if i % 2 == 0 if i % 3 == 0}
注意集合的特性,如果生成了不可hash的元素比如list,那么是不能生成集合的哦,如果元素重复,集合会去重的哦
{ 返回值(key:value) for 元素 in 可迭代对象 if 条件 }
{}
即可生成一个key为abcded的字典
{x:y for x in 'abcdef' for y in range(10)}
注意字典的key相同时,后面的赋值会把之前的值覆盖哦,所以结果是
{'a': 9, 'b': 9, 'c': 9, 'd': 9, 'e': 9, 'f': 9}
如果你是从上倒下看的,那么你可能会奇怪,说好的元组表达式呢?如果你是直接跳转过来的,那么请忽略前面这句话。那什么是生成器表达式呢?
生成器表达式是按需计算(或者惰性求值、延迟计算)的,只有需要的时候才计算值,而列表解析式是直接返回一个新的列表,生成器是一个可迭代对象
,迭代器
。在使用type命令判断对象类型时,generator
就表示一个生成器对象
()
即可延迟计算(惰性计算)
只能迭代一次,不能回头
g = ((i,j) for i in range(10) for j in range(20) if i < 3 if j > 18)
print(g) # at 0x7fca2552b258>
for i in g:
print(i) # 只能迭代一次,迭代完毕生成器就为空了哦,
没错,用括号括起来的并不是元组表达式,而变成了生成器表达式
,它本身由于惰性计算的特性和其他解析式有很多不同的特性
计算方式
内存占用
计算速度
遍历
除了遍历我们还可以通过
next方法
来一次一次的获取生成器的数据
In [8]: g = ((i,j) for i in range(10) for j in range(20) if i < 3 if j > 18)
In [9]: next(g)
Out[9]: (0, 19)
In [10]: next(g)
Out[10]: (1, 19)
In [11]: next(g)
Out[11]: (2, 19)
In [12]: next(g)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-12-e734f8aca5ac> in <module>
----> 1 next(g)
StopIteration:
In [13]:
next()可以理解为向生成器要一次数据(拨一下生成器),当生成器为空时,就会提示
StopIteration
异常,for循环帮我们对StopIteration异常做了处理,还没有学习异常处理的我们,该怎么办呢?其实next方法为我们提供了默认值参数
,即从生成器中拿不到数据,就返回指定的默认:next(g[, default])
。
In [13]: g = ((i,j) for i in range(10) for j in range(20) if i < 3 if j > 18)
In [14]: next(g, 'None')
Out[14]: (0, 19)
In [15]: next(g, 'None')
Out[15]: (1, 19)
In [16]: next(g, 'None')
Out[16]: (2, 19)
In [17]: next(g, 'None') # 生成器空了,就返回default指定的默认值
Out[17]: 'None'
In [18]: next(g, 'None')
Out[18]: 'None'
Python2 引入列表解析式,Python2.4引入生成器表达式,Python3 引入集合、字典解析式,并迁移到了Python 2.7,一般来说,应该多用解析式,简短、高效不过还需要注意的是:
从是否可迭代来看生成器、迭代器、可迭代对象的关系是如下