Python——生成器、列表生成式、迭代器

Python列表生成式

列表推导式的一般语法

[express for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
....
for itemn in iterablen if conditionn
]

这种语法等价于以下代码

s = []
for item1 in iterable1:
      if condition1:
          for item2 in iterable2:
              if condition2:
                ....
                for itemN in iterableN:
                    if conditionN:
                        expression

下面举一些列表推导式的栗子:

a = [1,3,5,7,9,10]
b = ['a','b','c','d','e']

# a列表中所有项*2
c = [2*s for s in a]
print(c)
# 带有条件的列表推导式
d = [s for s in a if s>=5]
print(d)

# 多item
e = [(x,y) for x in a for y in b if x > 0]
print(e)

  • Python中生成列表可以使用如下的表达式:
a = [x for x in range(10)]
# 0,1,2,3,4,5,6,7,8,9
  • 元组或列表赋值
t = ('128',222)
a,b = t
这里a,b将被赋值为t的两个元素
等价于 a = t[0] b =t[1]
print(a)
print(b)
# 128 222

生成器

通过列表生成式,我们可以直接创建一个列表,但是受到内存的限制,列表的容量是有限的。而且我们创建的数据过于庞大会占用很大的内存空间。所以,如果列表元素可以按照某种算法推算出来,我们就可以在循环的过程中不断推算后续的元素。这样就不必创建列表。从而节省了大量的空间。在Python中,我们称这种一边循环一边计算的机制,称为生成器 generator,生成器用来创建Python序列的一个对象。它可以迭代庞大的序列,且不需要在内存中创建和存储整个序列。任何使用yield的函数都称为生成器

生成器的创建

通过列表生成式创建

要创建一个generator,我们可以把列表生成式的[]改成一个()这样就创建了一个generator
生成器表达式是一个对象,它执行的计算与列表推导相同,但会迭代地生成结果。它的语法也与列表推导式相同,但要用圆括号代替方括号

  • 生成器表达式的语法
(expression for item1 in iterable1 if condition1 
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
)

生成器表达式和列表推导式不同,生成器表达式实际上不创建列表或者立即对圆括号内的表达式求值。相反,它会创建一个生成器对象,该对象通过迭代并按照需要生成的值,看个栗子

a = [1, 2, 3, 4]
b = (10 * i for i in a)
print(type(b))
print(b.__next__())
print(next(b))
>>>
<class 'generator'>
10
20

**使用生成器表达式和列表推导式可以极大地提高性能和n

L   = [x * x for x in range(10)]
L
g = (x*x for x in range(10))
g
# <generator object <genexpr> at 0x1022ef630>

这里L是一个list g是一个generator

使用yield

生成器本质上其实是一个函数,函数就会有返回值,我们在函数中使用yield

def gerator():
    print('ok')
    yield 1
    print('next')
    yield 2

g = gerator()
print(g)
print(next(g))
print(next(g))
######
<generator object gerator at 0x102210fc0>
ok
1
next
2

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句后返回。再次执行时从上次返回的yield语句处继续执行

我们这样定义函数后,直接执行这个函数不会被内部执行了,这个函数就会成为一个生成器对象
上面的生成器函数中,我们执行完第一个next中,程序会执行第一个yield的值,然后会记录执行位置,再执行第二个next时从上一次记录的位置继续执行,执行中遇到return,会认为函数的结束,那么生成器也会结束。

读取生成器的元素 next()

要生成的值并不在生成器中,而需要我们一个一个的去生成,可以通过next()函数获得生成器generator()的下一个返回值:

>>> next(g)

generator保存的是算法,所以每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
但是我们在使用generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心stopIteration的错误.for循环要求作用的对象是迭代对象,生成器就是一个迭代对象。所以可以直接使用for循环来取生成器的值

for i in g:
    print(i)

会在内部调用生成器的next()方法

执行迭代器的函数 send(p)

生成器也是函数,可以使用send(p)执行生成器函数并传递一个函数,这个函数会传递到yield的对象中。

  • 第一次使用send进入函数时只能传递一个None值,也就是说第一次send前如果没有next只能发送None数据

b.send(None)

def bar():
    print('before yield')
    count = yield 123
    print(count)
    yield 234
b = bar()

b.send(None)
b.send('abc')

Python 迭代器

在Python中,可以直接作用于for循环的数据类型可以分为:
1.集合数据类型 list、tuple、dict、set、str等
2.生成器generator 包括生成器和带yield的生成器函数
这些可以作用于for循环的对象统称为可迭代对象:Iterable
在Python中可以使用isinstance()判断一个对象是否是Iterable对象

from collections import Iterable

print(isinstance([], Iterable))
print(isinstance({}, Iterable))
print(isinstance((), Iterable))
print(isinstance('123', Iterable))
print(isinstance(100,Iterable))
#True
True
True
True
False
  • 判断是否是迭代器
    我们可以使用isinstance()判断一个对象是否是迭代器Iterator对象
from collections import Iterator

print(isinstance([], Iterator))
print(isinstance((), Iterator))
print(isinstance({}, Iterator))
print(isinstance('1111',Iterator))

我们可以看出list dict str tuple等数据类型虽然是Iterable,却不是Iterator,So
迭代器一定是可迭代对象,可迭代对象不一定是迭代器
那么为什么Python中 list dict str等数据类型不是Iterator?

因为Python中的Iterator对象表示的是一个数据流,而Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。这个数据流可以看做是一个有序序列。但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的的计算是惰性的,只有在需要返回下一给数据时才会计算
而Iterator可以表示一个无限大的数据流。
迭代器满足的条件:
1有iter方法
2有next方法

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

推荐阅读更多精彩内容