Python之列表解析式、生成器、迭代器及可迭代对象
语法糖
语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。
在python语言中语法糖有三元表达式、列表生成式、列表生成器、迭代器等等
解析式通用语法
为构造一个列表、集合或者字典,python提供了称谓”显式”的特殊语法,每种语法都有两种形式
1. 容器内容被明确列出(即普通常用的列表)
2. 它们通过一组循环和过滤指令来计算,称为生成式
语法格式如下:
1 | comprehension ::= expression comp_for |
File "<ipython-input-1-4d3e3e21b01e>", line 1
comprehension ::= expression comp_for
^
SyntaxError: invalid syntax
生成式是由单个表达式后紧跟至少一个for语句和多个或0个if语句
列表解析式
列表定义: 列表显示可能是方括号中包含的一系列空表达式
list_display ::= [ [starred_list | comprehension] ]
生成式基础语法格式:
[expr for iter_var in iterable] 即 [返回值 for 元素 in 可迭代对象]
工作过程:
- 迭代iterable中的每个元素;
- 每次迭代都先把结构赋值给iter_var,然后通过exp得到一个新的返回值;
- 最后将返回值形成一个新的列表。
代码示范:
1 | lst = [ x**2 for x in range(1,6)] # 保存5以内的阶乘 |
条件语法格式
[expr for item in iterable if cond1 if cond2]
工作过程:
迭代iterable中的每一个元素,然后对每个元素进行if条件判断,当有多个if时,if条件相当于if cond1 and if cond 2
将迭代的结果复制给item,然后通过expr表达式计算出返回值
将返回值形成新的列表
代码示范:
1 | lst = [x for x in range(1,20) if x >=10 if x %2==0] # 求 10到20间的偶数 |
[10, 12, 14, 16, 18]
嵌套循环语法
[expr for i in iterable1 for j in iterable2 ]
工作过程:
迭代iterable1中的第一个元素后,进入下一轮for循环迭代iterable2中的每一元素,interable2循环完成后,再次进入iterable1中的第二个元素,以此类推。
把迭代结果赋值给iter_var,荣光expr得到返回值
最后将返回值形成新的对象list
代码示范:
1 | lst = [ {x,y} for x in 'abc' for y in range(2) ] |
[{0, 'a'}, {'a', 1}, {'b', 0}, {'b', 1}, {0, 'c'}, {1, 'c'}]
列表生成式经典习题
- 求10以内的阶乘
1 | lst = [ x**2 for x in range(1,11)] |
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- 有一个列表lst = [1,4,9,16,2,5,10,15] 生成新列表,要求新列表元素是lst相邻2项的和
1 | lst = [1,4,9,16,2,5,10,15] |
[5, 13, 25, 18, 7, 15, 25]
- 打印九九乘法表
考点- 严格按照工作过程和列表解析式的定义,套用至少一个for循环或多个for循环
- for循环必须是连续在一起的,for循环不能分开
1 | lst = [ print('{}*{}={}'.format(j,i,j*i),end = '\n' if j == i else '\t' ) |
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
- “0001.abadicddws”是ID格式
要求ID格式是以点号分割,左边是4为从1开始的整数,右边是10位随机小写英文字母,请依次生成前100个ID的列表
1 | import string |
0001.pofdzgbvou
0002.qtncejuibm
0003.kabobmwrgf
0004.dxalxumcot
列表高阶函数经典
1.将字典转换成元组组成的列表
1 | dic = {'a':1,'b':2,'c':3} |
[('a', 1), ('b', 2), ('c', 3)]
- 把列表中所有字符转换小写,非字符串元素保留原样
1 | lst = ['abC34DRT','DE12Fav',10,'QQcom.com'] |
['abc34drt', 'de12fav', 10, 'qqcom.com']
- 把列表中所有的字符串转换小写,非字符串元素移除
1 | lst = ['abC34DRT','DE12Fav',10,'QQcom.com'] |
['abc34drt', 'de12fav', 'qqcom.com']
生成器
生成器迭代器是通过生成器生成的一个对象,每次遇到yield时会暂停生产值并记住位置记住位置执行状态(包括本地变量和挂起的try语句),
当生成器迭代器继续运行时,它会从中断的地方继续运行(与每次调用时都重新启动的函数相比)
其实生成器就是迭代器中的一种表现形式但是不同的对象,而且都是可迭代对象,
生成器的构成
生成器的构成是通过两种方式:
使用类似列表解析式生成
具体信息如下:
1.语法格式(返回值 for 元素 in 可迭代对象 if条件)
2.列表解析式的中括号换成小括号即可
3.返回一个生成器
使用包含yield的函数来生成
通常情况下对应简单的列表等采用列表生成器,若遇到复杂的计算值采用yield函数构成。
生成器的执行过程与特性
执行过程:在执行过程中,遇到yield关键字就会终端执行,下次继续从中断位置开始执行。
特性:
1.与列表解析式截然不同,列表解析式是立即返回一个完整的列表,生成器表达式是按需计算,惰性求值,需要是才进行求值;
2.遇到yield记录当前位置,下次从记录位置继续执行
3.从前到后走完一遍后,不能回头
生成器值的访问方式
通过内置next()方法
使用循环方式进行迭代
调用生成器对象send()方法
生成器表访问方式
1 | g = ("{:04}".format(i) for i in range(1,6)) |
0001
0002
0003
0004
0005
~~~~~~~~~~~~
生成器表达式返回值构成列表
expr表达式返回值构成列表或生成器,若无返回值时反回NoneTpye
1 | g = ( print('{},format(i+1)') for i in range(2)) |
{},format(i+1)
{},format(i+1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-20-cac278df07ae> in <module>
2 first = next(g)
3 second = next(g)
----> 4 val = first + second
5 print(val)
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'
生成器的next不能回头
1 | g = ( i for i in range(10) if i % 2 ) |
4
5
写生成器的两种方式
- 生成器表达式
1 | g = (x for x in range(10)) |
- yield语句
1 | def func(): |
列表解析式和生成器对比
计算方式
生成器表达式延迟计算,列表解析式立即计算
内存占用
但从返回值本身来说,生成器表达式省内存,列表解析式返回新的列表
生成器没有数据,内存占用极少 ,它是使用时一个个返回数据。如果将这些返回的数据合起来占用的内存也和列表解析式差不多。但是,它不需要立即占用这么多内存
列表解析式构造新的列表需要占用内存
计算速度
单看计算时间,生成器表达式耗时非常短,列表解析式耗时长
生成器本身并没有返回值,只返回一个生成器对象,虽然是返回值但是累加起来占用内存和列表生成式几乎相等
列表解析式构造并返回一个新的列表
集合解析式
语法:
{ 返回值 for 元素 in 可迭代对象 if 条件 }
列表解析式的中括号换成大括号{}就行了
立即返回一个集合
用法:
1 | s1 = {(x,x+1) for x in range(10)} |
{(0, 1),
(1, 2),
(2, 3),
(3, 4),
(4, 5),
(5, 6),
(6, 7),
(7, 8),
(8, 9),
(9, 10)}
字典解析式
语法:
{返回值 for 元素 in 可迭代对象 if 条件}
列表解析式的中括号换成大括号{}就行了
使用key:value形式
立即返回一个字典
用法:
1 | {x:(x,x+1) for x in range(10)} |
{'0': 3, '1': 3, '2': 3}
可迭代对象
可直接用于for循环的对象统称为可迭代对象(Iterable)
判断是否是可迭代对象,用函数isinstance()
1 | from collections import Iterable |
True
True
True
True
True
True
True
True
Iterable、Iterator和Generator的关系
生成器对象即是可迭代对象,也是迭代器,因为生成器既可以用for循环求值,也可以通过next()求值,直到抛出StopIteration时无法继续生成新值
迭代器对象一定时可迭代对象,可迭代对象不一定为迭代器,因为迭代器、生成器、可迭代对象都可以用for循环迭代求值,只有生成器和迭代器可以被next()方法求值