Python之列表解析式、生成器、迭代器及可迭代对象

Python之列表解析式、生成器、迭代器及可迭代对象

语法糖

语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。

在python语言中语法糖有三元表达式、列表生成式、列表生成器、迭代器等等

解析式通用语法

为构造一个列表、集合或者字典,python提供了称谓”显式”的特殊语法,每种语法都有两种形式
    1. 容器内容被明确列出(即普通常用的列表)
    2. 它们通过一组循环和过滤指令来计算,称为生成式

语法格式如下:

1
2
3
4
comprehension ::= expression comp_for
comp_for ::= “for” target_list “in” or_test [comp_iter]
comp_iter ::= comp_for | comp_if
comp_if ::= “if” expression_nocond [comp_iter]
  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
2
lst = [ x**2 for x in range(1,6)]    # 保存5以内的阶乘
print(lst)

条件语法格式

[expr for item in iterable if cond1 if cond2]
工作过程:
    迭代iterable中的每一个元素,然后对每个元素进行if条件判断,当有多个if时,if条件相当于if cond1 and if cond 2
    将迭代的结果复制给item,然后通过expr表达式计算出返回值
    将返回值形成新的列表
代码示范:
1
2
lst = [x for x in range(1,20)  if x >=10 if x %2==0]   # 求 10到20间的偶数
print(lst) # [10, 12, 14, 16, 18]
[10, 12, 14, 16, 18]

嵌套循环语法

[expr for i in iterable1 for j in iterable2 ]
工作过程:
    迭代iterable1中的第一个元素后,进入下一轮for循环迭代iterable2中的每一元素,interable2循环完成后,再次进入iterable1中的第二个元素,以此类推。
    把迭代结果赋值给iter_var,荣光expr得到返回值
    最后将返回值形成新的对象list
代码示范:
1
2
lst = [ {x,y} for x in 'abc' for y in range(2) ]     
print(lst) # [{0, 'a'}, {1, 'a'}, {0, 'b'}, {1, 'b'}, {0, 'c'}, {1, 'c'}]
[{0, 'a'}, {'a', 1}, {'b', 0}, {'b', 1}, {0, 'c'}, {1, 'c'}]

列表生成式经典习题

  1. 求10以内的阶乘
1
2
lst = [ x**2  for x in range(1,11)]
print(lst)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  1. 有一个列表lst = [1,4,9,16,2,5,10,15] 生成新列表,要求新列表元素是lst相邻2项的和
1
2
3
lst = [1,4,9,16,2,5,10,15]
nums = [ lst[i-1] + lst[i] for i in range(1,len(lst)) ]
print(nums)
[5, 13, 25, 18, 7, 15, 25]
  1. 打印九九乘法表
    考点
    • 严格按照工作过程和列表解析式的定义,套用至少一个for循环或多个for循环
    • for循环必须是连续在一起的,for循环不能分开
1
2
3
lst = [ print('{}*{}={}'.format(j,i,j*i),end = '\n' if j  == i else '\t' )  
for i in range(1,10)
for j in range(1,i+1) ]
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
  1. “0001.abadicddws”是ID格式
    要求ID格式是以点号分割,左边是4为从1开始的整数,右边是10位随机小写英文字母,请依次生成前100个ID的列表
1
2
3
4
import string
import random
alphabet = string.ascii_lowercase
lst = [ print( '{:04}.{}'.format(i ,''.join(random.choice(alphabet) for j in range(10) ) ) ) for i in range(1,5) ]
0001.pofdzgbvou
0002.qtncejuibm
0003.kabobmwrgf
0004.dxalxumcot

列表高阶函数经典

1.将字典转换成元组组成的列表

1
2
3
dic = {'a':1,'b':2,'c':3}
lst = [(x,y) for x,y in dic.items()]
print(lst)
[('a', 1), ('b', 2), ('c', 3)]
  1. 把列表中所有字符转换小写,非字符串元素保留原样
1
2
3
lst = ['abC34DRT','DE12Fav',10,'QQcom.com']
lst0 = [ x.lower() if isinstance(x,str) else x for x in lst ]
print(lst0)
['abc34drt', 'de12fav', 10, 'qqcom.com']
  1. 把列表中所有的字符串转换小写,非字符串元素移除
1
2
3
lst = ['abC34DRT','DE12Fav',10,'QQcom.com']
lst0 = [ x.lower() for x in lst if isinstance(x,str) ]
print(lst0)
['abc34drt', 'de12fav', 'qqcom.com']

生成器

生成器迭代器是通过生成器生成的一个对象,每次遇到yield时会暂停生产值并记住位置记住位置执行状态(包括本地变量和挂起的try语句),
当生成器迭代器继续运行时,它会从中断的地方继续运行(与每次调用时都重新启动的函数相比)
其实生成器就是迭代器中的一种表现形式但是不同的对象,而且都是可迭代对象,

生成器的构成

生成器的构成是通过两种方式:
    使用类似列表解析式生成
    具体信息如下:
        1.语法格式(返回值 for 元素 in 可迭代对象 if条件)
        2.列表解析式的中括号换成小括号即可
        3.返回一个生成器
     使用包含yield的函数来生成
    通常情况下对应简单的列表等采用列表生成器,若遇到复杂的计算值采用yield函数构成。

生成器的执行过程与特性

执行过程:在执行过程中,遇到yield关键字就会终端执行,下次继续从中断位置开始执行。
特性:
    1.与列表解析式截然不同,列表解析式是立即返回一个完整的列表,生成器表达式是按需计算,惰性求值,需要是才进行求值;
    2.遇到yield记录当前位置,下次从记录位置继续执行
    3.从前到后走完一遍后,不能回头

生成器值的访问方式

通过内置next()方法
使用循环方式进行迭代
调用生成器对象send()方法

生成器表访问方式

1
2
3
4
5
6
7
g = ("{:04}".format(i) for i in range(1,6)) 
print(next(g))
for x in g:
print(x)
print('~~~~~~~~~~~~')
for x in g:
print(x)
0001
0002
0003
0004
0005
~~~~~~~~~~~~

生成器表达式返回值构成列表

expr表达式返回值构成列表或生成器,若无返回值时反回NoneTpye

1
2
3
4
5
g = (  print('{},format(i+1)')  for i in range(2))
first = next(g)
second = next(g)
val = first + second
print(val)
{},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
2
3
4
5
6
g = (  i  for i in range(10) if i % 2 )
first = next(g)
second = next(g)
val = first + second
print(val)
print(next(g))
4
5

写生成器的两种方式

  1. 生成器表达式
1
g = (x for x in range(10))
  1. yield语句
1
2
def func():
yield

列表解析式和生成器对比

计算方式

生成器表达式延迟计算,列表解析式立即计算

内存占用

但从返回值本身来说,生成器表达式省内存,列表解析式返回新的列表
生成器没有数据,内存占用极少 ,它是使用时一个个返回数据。如果将这些返回的数据合起来占用的内存也和列表解析式差不多。但是,它不需要立即占用这么多内存
列表解析式构造新的列表需要占用内存

计算速度

单看计算时间,生成器表达式耗时非常短,列表解析式耗时长
生成器本身并没有返回值,只返回一个生成器对象,虽然是返回值但是累加起来占用内存和列表生成式几乎相等
列表解析式构造并返回一个新的列表

集合解析式

语法:
 { 返回值 for 元素 in 可迭代对象 if 条件 }
 列表解析式的中括号换成大括号{}就行了
 立即返回一个集合
用法:
1
2
3
s1 = {(x,x+1) for x in range(10)}
# s2 = {[x] for x in range(10)} # 集合内部不能添加不可哈希类型 即不可添加 列表或字典
s1
{(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
2
3
4
5
6
{x:(x,x+1) for x in range(10)}
{x:[x,x+1] for x in range(10)}
{(x,):[x,x+1] for x in range(10)}
# {[x]:[x,x+1] for x in range(10)} # 字典的key必须是课哈希类型
{chr(0x41+x):x**2 for x in range(10)}
{str(x):y for x in range(3) for y in range(4)} # 输出3个元素 字典的key需唯一
{'0': 3, '1': 3, '2': 3}

可迭代对象

可直接用于for循环的对象统称为可迭代对象(Iterable)
判断是否是可迭代对象,用函数isinstance()
1
2
3
4
5
6
7
8
9
10
from collections import Iterable

print(isinstance('ac',Iterable)) # True 字符串
print(isinstance('[]',Iterable)) # True 列表
print(isinstance('{}',Iterable)) # True 字典
print(isinstance('()',Iterable)) # True 元组
print(isinstance((x for x in range(2)),Iterable)) # True 生成器
print(isinstance([x for x in range(2)],Iterable)) # True 列表解析式
print(isinstance({x for x in range(2)},Iterable)) # True 集合解析式
print(isinstance({x:x+1 for x in range(2)},Iterable)) # True 字典解析式
True
True
True
True
True
True
True
True

Iterable、Iterator和Generator的关系

生成器对象即是可迭代对象,也是迭代器,因为生成器既可以用for循环求值,也可以通过next()求值,直到抛出StopIteration时无法继续生成新值

迭代器对象一定时可迭代对象,可迭代对象不一定为迭代器,因为迭代器、生成器、可迭代对象都可以用for循环迭代求值,只有生成器和迭代器可以被next()方法求值
-------------本文结束感谢您的阅读-------------
0%