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

你可能感兴趣的:(python の 可迭代、迭代器 、生成器)