7 - 列表解析式-生成器

1 解析式

        从一个问题来看解析式,现有以下需求:生成一个列表,元素0-9,对每个元素自增1后求平方返回新列表。app

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中列表解析式是一种语法糖,虽然对看似复杂的代码进行了简写,可是编译器会进行优化,不会由于简写而影响效率,反而由于优化提升了效率。另外还介绍了代码量,减小了出错的机会,还简化了代码,增长了代码可读性。学习

2 列表解析式

        列表解析式的基本语法是以下优化

[ 返回值 for 元素 in 可迭代对象 if 条件]
  • 使用中括号将表达式(推导式)括起来
  • 内部是for循环,if条件可选,能够是多个可是不支持elif语句
  • 返回一个新的列表

有这样的赋值语句 newlist = [ print(i) for i in range(10) ],请问newlist打印出来是什么?code

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?由于表达式只会将函数的返回值做为结果,进行添加,因此当返回值是一个函数操做的对象时,必定要注意函数的返回值!对象

2.1 列表解析式进阶

        有的时候咱们的代码须要进行两个或多个循环,列表解析式进阶版本能够知足这种需求哦。它的语法是:ip

[ 返回值 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个或多个]
  • 条件表达式能够是多个,可是不能是elif,多个if是而且的关系
  • 多个循环条件等同于循环嵌套,时间复杂度是O(n*内层循环个数)

例子:内存

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时,组成一个元素返回

3 其余解析式

        除了列表解析式之外,Python中还存在集合解析式字典解析式'元组解析式'

可不是什么元组解析式,这行小字你看不到,可不怪我哦。

3.1 集合表达式

  • 语法:{ 返回值 for 元素 in 可迭代对象 if 条件 }
  • 列表解析式的中括号换成大括号{}便可
  • 一样是当即返回一个集合
20之内,既能被2整除,又能被3整除的集合
{ i for i in range(20) if i % 2 == 0 if i % 3 == 0}

注意集合的特性,若是生成了不可hash的元素好比list,那么是不能生成集合的哦,若是元素重复,集合会去重的哦

3.2 字典解析式

  • 语法:{ 返回值(key:value) for 元素 in 可迭代对象 if 条件 }
  • 列表解析式的中括号换成大括号{}便可
  • 请使用key:value格式
  • 当即返回一个字典
生成一个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}

4 生成器表达式

        若是你是从上倒下看的,那么你可能会奇怪,说好的元组表达式呢?若是你是直接跳转过来的,那么请忽略前面这句话。那什么是生成器表达式呢?
        生成器表达式是按需计算(或者惰性求值、延迟计算)的,只有须要的时候才计算值,而列表解析式是直接返回一个新的列表,生成器是一个可迭代对象迭代器。在使用type命令判断对象类型时,generator 就表示一个生成器对象

  • 语法:( 返回值 for 元素 in 可迭代对象 if 条件表达式 )
  • 列表解析式的中括号换成大括号()便可
  • 延迟计算(惰性计算)
  • 只能迭代一次,不能回头
g = ((i,j) for i in range(10) for j in range(20) if i < 3 if j > 18)
print(g)  # <generator object <genexpr> at 0x7fca2552b258>
for i in g:
    print(i) # 只能迭代一次,迭代完毕生成器就为空了哦,

4.1 特色

        没错,用括号括起来的并非元组表达式,而变成了生成器表达式,它自己因为惰性计算的特性和其余解析式有不少不一样的特性

  1. 计算方式
            生成器表达式延迟计算(惰性计算),只有你去向它要,它才会给你计算,而列表解析式在你执行后,会直接给你生成一个新的列表。
  2. 内存占用
            生成器没有数据,内存占用极少,它是使用时一个一个返回数据,若是将这些返回的数据合起来占用的空间也和列表解析式差很少,可是它不是当即须要这么多空间
  3. 计算速度
            单看计算时间来看,生成器表达式耗时很是短,列表解析式时长,由于生成器自己并无任何返回值,只是返回了一个生成器对象,列表解析式构造并返回了一个新的列表,因此看起来更耗时了
  4. 遍历
            当咱们须要对数据进行遍历时,因为生成器是遍历一次计算一个返给你,而列表解析式执行完毕后直接返回一个新的列表不须要计算,因此性能要优于生成器表达式。

    4.2 next函数

    除了遍历咱们还能够经过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'

5 总结

        Python2 引入列表解析式,Python2.4引入生成器表达式,Python3 引入集合、字典解析式,并迁移到了Python 2.7,通常来讲,应该多用解析式,简短、高效不过还须要注意的是:

  • 若是一个解析式很是复杂,难以读懂,能够考虑拆成for循环,不必非要网列表解析式上靠
  • 生成器和迭代器是不一样的对象,但都是可迭代对象
  • 可迭代对象范围更大,均可以使用for循环遍历

从是否可迭代来看生成器、迭代器、可迭代对象的关系是以下
生成器

相关文章
相关标签/搜索