09-02解析式
解析式
举例场景:对一个列表所有的数值求平方
- 普通用法
In [15]: ret = []
...: for x in range(10):
...: ret.append(x ** 2)
...: print(ret)
...:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- 解析式用法
In [12]: [x ** 2 for x in list(range(10))]
Out[12]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
结论:解析式是比普通语句写法:性能更高,代码更简洁、可读性更强
的一种表达式。如果用起来就有如上优点、如果不用也可以用更麻烦的方式实现
解析式与普通用法法的性能及优势
- 列表解析的效率要高于普通用法
- 列表解析的代码更简洁、可读性强
例:列表解析与普通用法法的性能及优势
In [21]: import timeit
In [22]: %%timeit
...: ret = [ x ** 2 for x in range(10)]
...:
100000 loops, best of 3: 3.5 µs per loop
In [23]: %%timeit
...: ret = []
...: for x in range(10):
...: ret.append(x ** 2)
...:
100000 loops, best of 3: 3.88 µs per loop
总结:
列表解析的效率要高于普通用法
列表解析的代码更简洁、可读性强
解析式有如下几种
- 列表解析:[expr for e in iterator]
- 生成器解析:(expr for e in iterator)
- 集合解析:
- 字典解析:
它们都支持:
- 带if字句的列表解析:[expr for e in iterator if cond]
- 带单个if的列表解析:[expr for e in iterator if cond]
- 带任意多个的if字句:[ x for x in range(10) if x > 0 if x < 5 if x % 2 == 0]
列表解析
列表解析返回的都是列表,输入对象是所有可迭代对象。
- 带if字句的列表解析:[expr for e in iterator if cond]
- 带单个if的列表解析:[expr for e in iterator if cond]
- 带任意多个的if字句:[ x for x in range(10) if x > 0 if x < 5 if x % 2 == 0]
- 带for语句的列表解析
- 带单个for的列表解析
- 带多个for的列表解析
- 带if和for的列表解析
- 带单个if和for的列表解析
- 带多个if和for的列表解析
- 嵌套解析式(不建议使用)
带if字句的列表解析
解析式:[expr for e in iterator if cond]
例:带单个if的列表解析
In [25]: ret = []
...: for x in range(10):
...: if x % 2 == 0:
...: ret.append(x)
...: print(ret)
...:
[0, 2, 4, 6, 8]
In [26]: [x for x in range(10) if x % 2 == 0]
Out[26]: [0, 2, 4, 6, 8]
例:带任意多个的if字句
In [27]: [ x for x in range(10) if x > 0 if x < 5 if x % 2 == 0]
Out[27]: [2, 4]
带多个if语句的, 都可以转换为条件的逻辑运算, 所以一般来说, 不会带多个if语句。
带for语句的列表解析
例:可以有多个for语句, 相当于逐层嵌套
In [30]: [(x, y) for x in range(3) for y in range(5, 8)]
Out[30]: [(0, 5), (0, 6), (0, 7), (1, 5), (1, 6), (1, 7), (2, 5), (2, 6), (2, 7)]
In [32]: [(x, y, z) for x in range(2) for y in range(5, 7) for z in range(10, 12)]
Out[32]:
[(0, 5, 10),
(0, 5, 11),
(0, 6, 10),
(0, 6, 11),
(1, 5, 10),
(1, 5, 11),
(1, 6, 10),
(1, 6, 11)]
带多个if语句和for语句
例:多个for语句及if语句
In [33]: [(x, y) for x in range(3) if x > 0 for y in range(5, 8)]
Out[33]: [(1, 5), (1, 6), (1, 7), (2, 5), (2, 6), (2, 7)]
嵌套的列表解析
例:嵌套的列表解析
In [11]: [ x for x in [y for y in range(10)]]
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
难以预测结果, 所以一般不用
使用列表解析为了让代码更简洁
什么时候用代码解析式, 什么时候不用代码解析式, 跟着感觉走。什么时候不用代码解析式能简洁代码就什么时候不用。
能一下子看出输出结果时, 就用, 如果一眼看不出结果的解析式的结果就不要用了。
偶数求平方, 奇数求立方
例:偶数求平方, 奇数求立方
普通用法:
In [36]: ret = []
...: for x in range(10):
...: if x % 2 == 0:
...: ret.append(x ** 2)
...: else:
...: ret.append(x ** 3)
...: print(ret)
...:
[0, 1, 4, 27, 16, 125, 36, 343, 64, 729]
列表解析式用法
In [37]: [ x ** 2 if x % 2 == 0 else x ** 3 for x in range(10) ]
Out[37]: [0, 1, 4, 27, 16, 125, 36, 343, 64, 729]
列表解析的表达式
In [3]: x = 3
In [4]: x ** 2 if x % 2 == 0 else x ** 3
Out[4]: 27
x if cond else y # 当条件满足时返回x, 当条件不满足时返回y
In [15]: x ** 2 if x % 2 == 0 else x ** 3
Out[15]: 27
python 2.7 已经支持表达解析式
列表解析得到的结果是列表, 输入对象是所有可迭代对象。
生成器解析
生成器是什么:生成器是一次生成一个值的特殊类型函数。
Python3 迭代器与生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
- 列表解析的中括号变成小括号就是生成器解析了
- 生成器解析比列表解析的优势在于,当要生成的数特别大时列表解析会占用非常大的内存,而生成器解析不用
- 什么时候用列表解析, 什么时候用生成器解析?
- 需要用下标访问的时候, 用列表解析, 只需要对结果迭代的时候, 优先使用生成器解析。
例:生成器解析与列表解析的区别
In [16]: range(10000) # 当要生成的数特别大时
Out[16]: range(0, 10000)
In [17]: [ x ** 2 for x in range(10000)] # 会占用很多内存
In [18]: (x ** 2 for x in range(10000))
Out[18]: <generator object <genexpr> at 0x7fb8f0ed8f68> # 返回一个generator, 并且没有占用多少内存。
In [19]: type((x ** 2 for x in range(10000)))
Out[19]: generator
In [20]: g = (x ** 2 for x in range(10000))
In [21]: next(g) # 生成器解析调用的方法
Out[21]: 0
In [22]: next(g)
Out[22]: 1
总结:
列表解析的中括号变成小括号就是生成器解析了
生成器解析式返回的是一个生成器
例:表达式在取值的时候才开始计算
In [23]: def fn(x):
...: print('executed')
...: return x
...:
In [24]: g = (fn(x) for x in range(10))
In [25]: next(g)
executed
Out[25]: 0
In [26]: next(g)
executed
Out[26]: 1
In [27]: next(g)
executed
Out[27]: 2
什么时候用列表解析, 什么时候用生成器解析?
需要用下标访问的时候, 用列表解析, 只需要对结果迭代的时候, 优先使用生成器解析。
集合解析
- 集合解析返回的是集合
- 集合解析式用大括号
In [1]: {x for x in range(10)}
Out[1]: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
In [2]: s = {x for x in range(10)}
In [3]: type(s)
Out[3]: set
总结:
集合解析返回的是集合
集合解析式用大括号
字典解析
- 字典解析式:key的表达式和value的表达式组成
例:字典解析表达
In [4]: {str(x): x for x in range(10)}
Out[4]:
{'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9}
{str(x): x for x in range(10)} # : 冒号之前是key的表达式, 冒号之后的是value的表达式
例:普通表达式
In [8]: d = {}
...: for x in range(10):
...: d[str(x)] = x
...:
In [9]: d
Out[9]:
{'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9}
例:字典解析
In [82]: {x:y for y, x in {'1': '2', '3': '4'}.items()}
Out[82]: {'2': '1', '4': '3'}
字典解析式:key的表达式和value的表达式组成