列表解析式、生成器、迭代器及可迭代对象的区别和应用

导文

  • 语法糖(Syntactic sugar)
  • 列表生成式(list comprehension)
  • 生成器(generator)
  • 迭代器(iterator)
  • 可迭代对象(iterable)
  • Iterable、Iterator与Generator之间的关系

语法糖

语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。
在python语言中语法糖有三元表达式、列表生成式、列表生成器、迭代器等等,具体可参考如下博客:

 https://segmentfault.com/a/1190000006261012

解析式通用语法

For constructing a list, a set or a dictionary Python provides special syntax called “displays”, each of them in two flavors:
为构造一个列表、集合或者字典,python提供了称谓”显式”的特殊语法,每种语法都有两种形式
1、either the container contents are listed explicitly
容器内容被明确列出(即普通常用的列表)
2、they are computed via a set of looping and filtering instructions, called a comprehension
它们通过一组循环和过滤指令来计算,称为生成式
Common syntax elements for comprehensions are:
语法格式如下:
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]
The comprehension consists of a single expression followed by at least one for clause and zero or more for or if clauses.
生成式是由单个表达式后紧跟至少一个for语句和多个或0个if语句

列表生成式

列表定义

A list display is a possibly empty series of expressions enclosed in square brackets:
list_display ::= “[” [starred_list | comprehension] “]”

生成式语法

基础语法格式

[expr for iter_var in iterable] 即 [返回值 for 元素 in 可迭代对象]
工作过程:
- 迭代iterable中的每个元素;
- 每次迭代都先把结构赋值给iter_var,然后通过exp得到一个新的返回值;
- 最后将返回值形成一个新的列表。
代码示范:

#!/bin/python3
#-*- coding: UTF-8 -*-
lst = [x*x for x in range(1,6)]
print(lst)
运行结果:
[1, 4, 9, 16, 25]

条件语法格式

[expr for item in iterable if cond1 if cond2]
工作过程:

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

嵌套循环语法

[expr for i in iterable1 for j in iterable2 ]
工作过程:

  • 迭代iterable1中的第一个元素后,进入下一轮for循环迭代iterable2中的每一元素,interable2循环完成后,再次进入iterable1中的第二个元素,以此类推。
  • 把迭代结果赋值给iter_var,荣光expr得到返回值
  • 最后将返回值形成新的对象list
    代码示范:
#!/bin/python3
#-*- coding: UTF-8 -*-
lst0 = [(x,y) for x in 'abc' for y in range(2)]
lst1 = [[x,y] for x in 'abc' for y in range(2)]
lst2 = [{x,y} for x in 'abc' for y in range(2)]
print(lst0)
print(lst1)
print(lst2)
运行结果:
[('a', 0), ('a', 1), ('b', 0), ('b', 1), ('c', 0), ('c', 1)]
[['a', 0], ['a', 1], ['b', 0], ['b', 1], ['c', 0], ['c', 1]]
[{
    0, 'a'}, {
    1, 'a'}, {
    0, 'b'}, {
    1, 'b'}, {
    0, 'c'}, {
    1, 'c'}]

列表生成式经典习题

返回1-10平方的列表

代码示范:

#!/bin/python3
#-*- coding: UTF-8 -*-
lst = [x**2 for x in range(1,11)]
print(lst)
运行结果:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

有一个列表lst = [1,4,9,16,2,5,10,15]

生成新列表,要求新列表元素是lst相邻2项的和
代码示范:

#!/bin/python3
#-*- coding: UTF-8 -*-
lst = [1,4,9,16,2,5,10,15]
lstnew = [lst[i]+lst[i+1] for i in range(len(lst)-1)]
print(lstnew)
运行结果:
[5, 13, 25, 18, 7, 15, 25]

打印九九乘法表

考点
- 严格按照工作过程和列表解析式的定义,套用至少一个for循环或多个for循环
- for循环必须是连续在一起的,for循环不能分开
正确示范代码:

#!/bin/python3
#-*- coding: UTF-8 -*-
lst = [print("{}*{}={:<3}{}".format(j,i,j*i,"\n" if i==j else ""),end="") for i in range(1,10) for j in range(1,i+1)]
print(lst)
运行结果:
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

切忌for j循环写入format中,如果将j循环写入format中时,违反了代码语法:[expr for iter_val in iterable for iter_val in iterable],若违反代码语法肯定报错,根据工作过程知道,首先进行循环i,迭代到1时,返回给expr表达式print(),print的返回值构成新的列表。
另外:最后一个for循环表示要出现的次数,下面错误代码中示范的案例表明只会出现10个相乘的数,因此和题意完全不符,
错误示范代码:

#!/bin/python3
#-*- coding: UTF-8 -*-
lst = [print("{}*{}={:<3}{}".format(j,i,j*i,"\n" if i==j else ""),end="") for i in range(1,10) for j in range(1,i+1)]
print(lst)
运行结果:
SyntaxError: Generator expression must be parenthesized if not sole argument
SyntaxError:如果不是唯一参数,则必须将生成器表达式加括号

“0001.abadicddws”是ID格式

要求ID格式是以点号分割,左边是4为从1开始的整数,右边是10位随机小写英文字母,请依次生成前100个ID的列表

#!/bin/python3
#-*- coding: UTF-8 -*-
import random
string = "abcdefghigklmnopqrstuvwsyz"
lst = ["{:04}.{}".format(i,"".join(string[random.randint(0,25)] for j in range(10))) for i in range(1,4)]
print(lst)
运行结果:
['0001.mgdpsgtcqa', '0002.kduzdfncds', '0003.itohqzghzk']

代码对比说明问题

#嵌套正确代码
string = "abcdefghigklmnopqrstuvwsyz"
lst = ["{:04}.{}".format(i,"".join(string[random.randint(0,25)] for j in range(10))) for i in range(1,4)]
print(lst)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#嵌套非正确代码
lst = ["{}*{}".format(i,j for j in range(1,i+1)) for i in range(1,10)]
print(lst)
#嵌套说明问题
当生成i*j个的expr表达式时,嵌套for时,两个for时兄弟并联关系
当生成i个expr表达式的值时,在expr表达式内可以可以嵌套for循环,但是expr表达式内的for循环和语法糖中的for循环毫无关系

列表高阶函数经典

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

lst={‘Tom’:15,’Jerry’:18,’Peter’:13}

#!/bin/python3
#-*- coding: UTF-8 -*-
dic = {
    'Tom':15,'Jerry':18,'Peter':13}
lst = [(x,y) for x,y in dic.items()]
print(lst)
运行结果:
[('Jerry', 18), ('Tom', 15), ('Peter', 13)]

把列表中所有字符转换小写,非字符串元素保留原样

lst = [‘TOM’,’Peter’,10,’Jerry’]

#!/bin/python3
#-*- coding: UTF-8 -*-
lst = ['TOM','Peter',10,'Jerry']
lst0 = [x.lower() if isinstance(x,str) else x for x in lst]
lst1 = list(map(lambda x:x.lower() if isinstance(x,str) else x,lst))
print(lst0)
print(lst1)
运行结果:
['tom', 'peter', 10, 'jerry']
['tom', 'peter', 10, 'jerry']

把列表中所有的字符串转换小写,非字符串元素移除

lst = [‘TOM’,’Peter’,10,’Jerry’]

#!/bin/python3
#-*- coding: UTF-8 -*-
lst = ['TOM','Peter',10,'Jerry']
lst0 = [x.lower() for x in lst if isinstance(x,str)]
lst1 = list(map(lambda x:x.lower(),filter(lambda x: isinstance(x,str),lst)))
print(lst0)
print(lst1)
运行结果:
['tom', 'peter', 'jerry']
['tom', 'peter', 'jerry']

生成器(generator)

生成器定义

A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.
返回生成器迭代器的函数。 它看起来像一个普通的函数,只是它包含yield表达式,用于生成一系列可用于for-loop的值,或者可以使用next()函数一次检索一个值。

生成器

generator iterator
An object created by a generator function,Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks-up where it left-off (in contrast to functions which start fresh on every invocation).
生成器迭代器是通过生成器生成的一个对象,每次遇到yield时会暂停生产值并记住位置,当再次遇到迭代时它会在从暂停的位置重新开始,
其实生成器就是迭代器中的一种表现形式但是不同的对象,而且都是可迭代对象,后面有实验证明[在迭代器判断中]

生成器的构成

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

生成器的执行过程与特性

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

生成器值的访问方式

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

实例说明

生成器表访问

代码及特性示范1:

#!/bin/python3
#-*- coding: UTF-8 -*-
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
~~~~~~~~~~~~

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

代码示范2:
expr表达式返回值构成列表或生成器,若无返回值时反回NoneTpye

#!/bin/python3
#-*- coding: UTF-8 -*-
#val的值是什么?
#val=first+second语句之后能否再next()?
g = (print('{}'.format(i+1)) for i in range(2))
first = next(g)
second = next(g)
val = first + second
print(val)
print(next(g))
运行结果:
1
2
Traceback (most recent call last):
  File "./gen.py", line 9, in 
    val = first + second
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'

生成器的next不能回头

代码示范3:

#!/bin/python3
#-*- coding: UTF-8 -*-
#val的值是什么?
#val=first+second语句之后能否再next()?
g = (x for x in range(10) if x % 2)
#取奇数1,3,5,7,9 first=1 second=3 val=4 下一个为5
first = next(g)
second = next(g)
val = first + second
print(val)
print(next(g))
运行结果:
4
5

写生成器的两种方式

生成器表达式

g = (x for x in range(10))

yield语句

def func():
yield

列表解析式和生成器对比

计算方式

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

内存占用

  • 但从返回值本身来说,生成器表达式省内存,列表解析式返回新的列表
  • 生成器没有数据,内存占用极少
  • 列表解析式构造新的列表需要占用内存

计算速度

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

实验论证

#!/bin/python3
#-*- coding: UTF-8 -*-
import datetime
import sys
start = datetime.datetime.now()
lst = [ x for x in range(10000000) ]
delta = (datetime.datetime.now() - start).total_seconds()
print("列表解析式耗时:{}".format(delta))
print("列表解析式耗内存:{}".format(sys.getsizeof(lst)))
start0 = datetime.datetime.now()
lst = ( x for x in range(10000000) )
delta0 = (datetime.datetime.now() - start0).total_seconds()
print("列表生成式耗时:{}".format(delta0))
print("列表生成式耗内存:{}".format(sys.getsizeof(lst)))
start1 = datetime.datetime.now()
def test_gen(start,end):
    for x in range(start,end):
        yield x
lst =test_gen(0,10000000)
delta1 = (datetime.datetime.now() - start1).total_seconds()
print("列表生成器函数耗时:{}".format(delta1))
print("列表生成器函数耗内存:{}".format(sys.getsizeof(lst)))
运行结果:
列表解析式耗时:0.328316
列表解析式耗内存:81528056
列表生成式耗时:0.104485
列表生成式耗内存:88
列表生成器函数耗时:1.2e-05
列表生成器函数耗内存:88

可迭代对象

可直接用于for循环的对象统称为可迭代对象(Iterable)
判断是否是可迭代对象,用函数isinstance()

#!/bin/python3
#-*- coding: UTF-8 -*-
from collections import Iterable
print(isinstance("abc",Iterable))
print(isinstance("[]",Iterable))
print(isinstance("{}",Iterable))
print(isinstance("()",Iterable))
print(isinstance("(x,for x in range(2))",Iterable))
运行结果:
True
True
True
True
True

迭代器

迭代器定义

An object representing a stream of data. Repeated calls to the iterator’s next() method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its next() method just raise StopIteration again.
表示数据流的对象。 重复调用迭代器的next ()方法(会返回流中的连续项。直到没有数据可以返回时抛出StopIteration异常错误。可以把这个数据流看做一个有序序列,但我们无法提前知道这个序列的长度。同时,Iterator的计算是惰性的,只有通过next()函数时才会计算并返回下一个数据。

迭代器判断

通过定义及判断可知,生成器也是迭代器,但是不是同一个对象,通过type返回值不一样

#!/bin/python3
#-*- coding: UTF-8 -*-
from collections import Iterator
print(isinstance("abc",Iterator))
print(isinstance("()",Iterator))
print(isinstance((x for x in range(2)),Iterator))

Iterable、Iterator和Generator的关系

  • 生成器对象即是可迭代对象,也是迭代器,因为生成器既可以用for循环求值,也可以通过next()求值,直到抛出StopIteration时无法继续生成新值
  • 迭代器对象一定时可迭代对象,可迭代对象不一定为迭代器,因为迭代器、生成器、可迭代对象都可以用for循环迭代求值,只有生成器和迭代器可以被next()方法求值

你可能感兴趣的:(python,python)