python の 可迭代、迭代器 、生成器

  • 生成器:

生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__ iter__和__ next __方法(python2 是 def next(self)),是一个用于迭代器类的写法,即 直接返回self(即自己本身),然后 定义 __ next __,在 _ next _ 直接不断返回东西就行了如下:)

class xx():
    def __iter__(self):
        return self       # 返回该对象的**迭代器类的实例**;因为自己就是迭代
#器,所以返回self,返回self 之后 就会转移到 _ next _ 函数了,相当于 return  
#self.__next__ () 一样
#当然__ iter__ 里面也可以直接返回迭代器 如 yield  或 内置函数  iter() 实现的迭
#代器,都是棒棒哒
 
    def __next __(self):              # 迭代器类必须实现的方法,每次调用都相当于
# 是临时计算了一个新的值,对不对
        while self.now < self.data:
            self.now += 1
            return self.now - 1  # 返回当前迭代值
        raise StopIteration      # 超出上边界,抛出异常

这种一边循环一边计算的机制,称为生成器:generator
基本有两种形式的生成器
你也许见过列表生成器,如 [ x for x in range(5) ] ,他的结果是一个 列表 ,但是注意,当这个结果里的元素很多,设置接近无限多的时候,你的内存就撑不住了。所以引出另一种解决办法,生成器

生成器有两种定义方法:

1、普通生成器 把列表生成式的 [ ] 改成 ()

如:(x for x in range(5)) # 注意这里不是元祖哦


第一种 生成器

只要是生成器,就可以用 next(generator) 来不断获取生成器的下一个值
如下:


next() 函数取出生成器的值

使用next() 到没有值返回是,会爆出 StopIteration 错误,生成器也就结束了(使命就完成了)
但是通常使用 next() 有点麻烦,还要捕获最后的异常,所以通过for 循环比较方便一点
因为generator也是可迭代对象,可迭代对象就可以使用for 循环
第一种生成器比较简单,但是如果有复杂的逻辑,就不好使了

2、函数生成器

如果一个函数中有 yield 关键字,这个函数就不是普通的函数了,他是一个生成器函数
每次调用 yiled 的时候都会返回 起后面的值,和return 很像,但是并不会终止整个函数,就好像按了暂停键。
同样这个也可以通过 用next()函数不断获得下一个返回值:


next() 读函数生成器
for 读取 函数生成器
  • 可迭代对象

直接作用于for循环的对象统称为可迭代对象:Iterable
如 :一类是 :list dict str set tuple
二类是 :生成器
isinstance()判断一个对象是否是Iterable对象:

判断一个对象是不是可迭代对象

  • 迭代器

可以用 next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
isinstance()判断一个对象是否是Iterator对象:

from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

可迭代对象中,生成器是 迭代器,但是那些基本类型 list ... 却不是,他们只是可迭代对象(里面定义了 __ iter __() 魔法函数,可以使用内置方法 iter(xx) 返回他们 的生成器

python提供了一个iter函数用来生成迭代器。这个方法有两个参数,当只有一个参数的时候,若这个参数是一个容器,则返回这个容器的迭代器对象,若这个参数本身就是一个迭代器,则返回其自身。

为毛 list、dict、str等数据类型不是Iterator
我也有个疑问,看看廖雪峰大神的讲解
iterator 是一个有序序列的数据流,可以被next() 调用,直到没有数据时抛出StopIteration错误。我们并不能提前知道他的长度。只有在用的时候惰性计算当前值。list 这些显然里面的元素,都是已知 的。不属于迭代器

最后提一点,能用 next() 的不一定可以用 for 读取,能用 for 的不一定能用next(),但其实for 会把 (可迭代对象)变成 可以用 next() 的迭代器,所以for 底层还是调用 next() 来读取值的

最后说一下 for 循环的本质:
for 最终 是读取 循环读取的迭代器,可迭代对象会先转为迭代器,然后在next(xx) 读取

iter()是直接调用该对象的iter(),并把iter()的返回结果作为自己的返回值
iter函数可以显示调用,或当执行“for i in obj:”,Python解释器会在第一次迭代时自动调用iter(obj),( 如果__ iter __ 返回self ,则 迭代相当于 调用迭代器的__ next __方法),for语句会自动处理最后抛出的StopIteration异常。

for x in [1, 2, 3, 4, 5]:
    pass

上面的真实处理过程是这样的:

for x  in   iter([1, 2, 3, 4, 5]):
    print(x)
>1
  2..

更进一步,更为底层的剖析:

# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

上面就是for 的真实工作流程了

对象本身有 __ iter __ 并且 __ iter __ 返回一个迭代器(next 的对象) 则他是可迭代的,如果 __ iter __返回当前对象本身(本身必须有 __next __ 否则不是迭代器,就报错)那么这个对象本身就是一个迭代器。 有了 __ next __,就可以被next() 函数 循环调用,于是也可以通过for 调用了。啦啦啦。现在总算搞清楚了这三者的关系
大致关系就是 :

可迭代 / 迭代器 》 生成器

可迭代 用 iter() 转为 迭代器 ,生成器是一个特殊的迭代器,总之内部也实现了 _ iter_ 和 _ next _ ,只不过方式不同,( 比如 有了 yield 也是迭代器, 函数执行状态,遇到 yield 会自动生成 __ next __ 并运行 他 返回 yield 后面的值 )

函数执行状态会 才是一个 迭代器,因为执行时,碰到 yield 才会触发,产生 _ next_ 和_ iter_

执行状态是迭代器
函数本身依然不是迭代器

注意

def xx(a):
    print(123)
    yield 222
    return 1234

注意上述代码
b = xx("")
b 的值不是return 的1234 ,而是一个迭代器函数对象,而且,print(123) 也没有执行,只有 b.next() 的时候才会执行print(123)
另外函数带有yield ,则如果函数里面有 return ,那么只会终止 迭代器,并引发 StopIteration

StopIteration

另外 使用 yield 的时候,可以用send 来返回 yield 的接收值 如:

def fun(): 
    print ('start...')
    m = yield 5
    print (m)
    print ('middle...')
    d = yield 12
    print (d) 
    print ('end...')
m = fun()
# 调用 send()
next(m)  或 m.__next__()
m.send(1)

start...
1
middle...
>>12
# 不调用send()  
m.__next__()
start...
>> 5
def fun(): 
    for i in range(5):
        a = yield i
        print(a)
m = fun()
for i in  m:
    m.send("222")
    print(i)
    print("#########")
结果:

222
0
#########
None
222
2
#########
None
222

可以看出 send()具有解堵塞的作用,他会直接对yield 的地方往下执行,再执行一个 yield ,可以认为 send(None) 等价于 一个 next()。 这就解释了,为什么用send() 会直接跳过一个 yield,因为他本身是一个可以传值的 next()

同样带有 yield 的对象,可以在每次调用next 之后随即,send(xx) 返回到含数内部。没有send 默认 next 传递 None


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

推荐阅读更多精彩内容