-
生成器:
生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__ 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() 到没有值返回是,会爆出 StopIteration 错误,生成器也就结束了(使命就完成了)
但是通常使用 next() 有点麻烦,还要捕获最后的异常,所以通过for 循环比较方便一点
因为generator也是可迭代对象,可迭代对象就可以使用for 循环
第一种生成器比较简单,但是如果有复杂的逻辑,就不好使了
2、函数生成器
如果一个函数中有 yield 关键字,这个函数就不是普通的函数了,他是一个生成器函数
每次调用 yiled 的时候都会返回 起后面的值,和return 很像,但是并不会终止整个函数,就好像按了暂停键。
同样这个也可以通过 用next()函数不断获得下一个返回值:
-
可迭代对象
直接作用于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 后面的值 )
注意
def xx(a):
print(123)
yield 222
return 1234
注意上述代码
b = xx("")
b 的值不是return 的1234 ,而是一个迭代器函数对象,而且,print(123) 也没有执行,只有 b.next() 的时候才会执行print(123)
另外函数带有yield ,则如果函数里面有 return ,那么只会终止 迭代器,并引发 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