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的表达式组成
                    
                
                
            
        
浙公网安备 33010602011771号