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的表达式组成
posted @ 2020-06-04 06:59  此时  阅读(164)  评论(0编辑  收藏  举报