Python篇-列表生成式、生成器、迭代器与可迭代对象

列表生成式、和生成器、迭代器与可迭代对象

看py代码的时候,每次遇到类似列表生成式与生成器(generator)相关内容的时候,都需要去百度,一次两次三次,每次都不太能记住他们的用法,这里做一个简单总结。

一、列表生成式

按照博大精深的中华文化来理解一下字面意思:它是一个表达式,什么表达式?用来生成列表的一个表达式。

[exp for iter_var in iterable]

嗯,看起来与匿名函数lambda有几分神似。工作过程是这样的:
1、迭代iterable中的每个元素
2、把这些元素依次赋值给iter_var
3、然后通过表达式exp作用于步骤2的每一个值,并生成一个新的元素
4、把步骤3得到的新的元素组成一个新的列表返回
类似于下面这个for循环:

list1 = []
def exp(arg):#这就是exp表达式
    return arg**2#表达式的作用是计算入参的2次方
for i in range(5):
    list1.append(exp(i))
print(list1)

这一段代码可以用列表生成式实现:

list1 = [x**2 for x in range(5)]
print(list1)

看起来是不是简单很多?当然列表生成式的作用不仅仅是这个。列表生成式还可以带过滤条件:

list1 = [x**2 for x in range(5) if x%2 == 0]#增加了一个if表达式,意思是只计算能被2整除的数的2次方
print(list1)

工作过程在上面的基础上增加了一步:
1、迭代iterable中的每个元素(此处为range(5))
2、把这些元素依次赋值给iter_var(x),然后判断if表达式是否成立,成立则进行下一步,不成立则进行下一次迭代
3、然后通过表达式exp(x**2)作用于步骤2的每一个值,并生成一个新的元素
4、把步骤3得到的新的元素组成一个新的列表返回
同样,类似于下面的过程:

list1 = []
def exp(arg):#这就是exp表达式
    return arg**2#表达式的作用是计算入参的2次方
for x in range(5):
    if x%2 == 0:
        list1.append(exp(x))
print(list1)
例子

下面有几个例子,实现方式是“使用列表生成式”和“不适用列表生成式”,看下区别:
eg1:生成一个从3到10的数字列表:

#不使用列表生成式:
list1 = list(range(3,11))
print(list1)
#或
list2 = []
for i in range(3,11):
    list2.append(i)
print(list2)
#使用列表生成式
list3 = [x for x in range(3,11)]
print(list3)

eg2:生成一个2n+1的数字列表,n为从3到11的数字

#不使用列表生成式
list4 = []
for n in range(3,11):
    list4.append(2*n+1)
#使用列表生成式
list4 = [2*n+1 for n in range(3,11)]
print(list4)

eg3:计算两个集合的全排列,并将结果作为保存至一个新的列表中

#不使用列表生成式
L1 = ['香蕉', '苹果', '橙子']
L2 = ['可乐', '牛奶']
list5 = []
for m in L1:
    for n in L2:
        list5.append((m,n))
print(list5)
#使用列表生成式
L1 = ['香蕉', '苹果', '橙子']
L2 = ['可乐', '牛奶']
list5 = [(m,n) for m in L1 for n in L2]
print(list5)

eg4:将一个字典转换成由一组元组组成的列表,元组的格式为(key, value)

#不使用列表生成式
D = {'Tom': 15, 'Jerry': 18, 'Peter': 13}
list6 = []
for k,v in D.items():
    list6.append((k,v))
print(list6)
#使用列表生成式
D = {'Tom': 15, 'Jerry': 18, 'Peter': 13}
list6 = [(k,v) for k,v in D.items()]
print(list6)

二、map函数和filter函数

map() 会根据提供的函数对指定序列做映射;
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表;
举个例子:
map()

def square(x):
    return(x**2)
r = list(map(square,[1,2,3,4,5]))#map函数返回的是一个对象,需要把这个对象转化成列表,然后打印
print(r)#输出[1, 4, 9, 16, 25]
#用匿名函数实现:
L = [1,2,3,4,5]
r = list(map(lambda x:x**2,L))
print(r)

filter()

def is_odd(x):
    return(x % 2 == 1)
r = list(filter(is_odd,[1,2,3,4,5]))
print(r)#输出[1, 3, 5]
#用匿名函数实现:
L = [1,2,3,4,5]
r = list(filter(lambda x:x % 2 == 1,L))
print(r)

还有两个稍微难一点的例子:
eg5:把一个列表中所有的字符串转换成小写,非字符串元素原样保留

L = ['TOM', 'Peter', 10, 'Jerry']
# 用列表生成式实现
list1 = [x.lower() if isinstance(x, str) else x for x in L]

# 用map()函数实现
list2 = list(map(lambda x: x.lower() if isinstance(x, str) else x,  L))

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

L = ['TOM', 'Peter', 10, 'Jerry']
# 用列表生成式实现
list3 = [x.lower() for x in L if isinstance(x, str)]

# 用map()和filter()函数实现
list4 = list(map(lambda x: x.lower(), filter(lambda x: isinstance(x, str), L)))

三、生成器(Generator)

生成器的原理是,按照一种特定的逻辑算法,在调用他时,不断生成后续相应的数据,不调用则不生成。比如生成100万个数字的列表,某种情况下,我们不需要一次全部生成这些数据,而是用到的时候才需要让他生成,这种情况下,如果使用列表生成式或者一次性计算100万个数字的算法的话,就会浪费内存空间,也会使程序的运行时间变长。这个时候可以用到生成器。
生成器的第一种构造方式,可以直接使用列表生成式来构造,只需要把列表生成式的[]变成()就好。比如:

g1 = (2*n + 1 for n in range(3, 6))

也可以使用包含yield关键字的函数来构造(包含yield关键字的函数都叫做生成器),比如

def my_range(start, end):
    for n in range(start, end):
        yield 2*n + 1
g2 = my_range(3, 6)

可以使用python的type函数,来检验g1、g2是否是生成器

print(type(g1))#输出为:<class 'generator'>
print(type(g2))#输出为:<class 'generator'>

生成器的执行过程:
在函数执行过程中, 遇到yield关键字会中断执行,知道下次调用函数时,在开始从中断的位置开始继续执行。
生成器的常用调用方式:
1、调用next()方法
2、使用循环对生成器对象进行遍历
使用next()方法调用:

print(next(g1))
print(next(g1))
print(next(g1))
print(next(g1))

输出为:

7
9
11
Traceback (most recent call last):
  File "***/generator.py", line 26, in <module>
    print(next(g1))
StopIteration
print(next(g2))
print(next(g2))
print(next(g2))
print(next(g2))

输出为:

7
9
11
Traceback (most recent call last):
  File "***/generator.py", line 31, in <module>
    print(next(g2))
StopIteration

当遍历完生成器,再调用next()函数,程序会抛出一个StopIteration的异常
使用循环遍历的方法调用:

for x in g1:
    print(x)

for x in g2:
    print(x)

上述生成结果时一样的

7
9
11

所以,使用循环遍历的方式,最终不会抛出异常,因为for循环结束的时候,就不会再继续循环了,也就不会再继续调用生成器了,也就不会抛出异常。
最后补充一下关于迭代器(iterator),可迭代对象(iterable),和生成器之间的关系。
1、可以被类似next()这样的函数不断调用,并且不断返回下一个值的对象称为迭代器
2、可迭代对象,类似python基础中学到的列表(list),元组(tuple),字典(dict),字符串(str)都是可以迭代的对象,他们都可以用for循环进行遍历
3、生成器就是一种迭代器
所以,迭代器和生成器一定是可迭代对象,反之不一定。比如list,dict,str等;他们三者也都可以用for循环去循环遍历,但是生成器和迭代器(或许不应该这样说,因为生成器本身就是迭代器的一种)还可以使用next()方法调用并不断返回下一个值。
注:上文引用自云游道士的博客(地址:https://www.cnblogs.com/yyds/p/6281453.html),写的很好,在此借鉴一下。有什么谬误也欢迎指出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349