解释python的生成器,迭代器,和 for … in …语法糖
for ... in ...
语法糖
for... in...
做了什么事情?产生一个迭代器(),并调用__next__
方法逐一获得值
for value in iter_obj:
<do something>
等同于:
iter_obj_itor = iter(iter_obj) # 返回一个迭代器
while True:
try:
value = iter_obj_itor.__next__()
<do something>
except:
raise StopIteration
- 其中,
iter
方法返回了一个迭代器iterator(定义在后面解释),然后通过__next__
方法不断取得迭代器对象的当前值;iter()
函数等同于:
def iter(obj):
return obj.__iter__() # 返回了一个迭代器
- next() 函数等同于
def next(obj):
return obj.__next__()
for ...in...
举例:
lst = [1,2,3]
lst_ = []
for v in lst:
lst_.append(v)
#等价于:
lst = [1,2,3]
lst_ = []
lst_iterator = lst.__iter__()
while True:
v = lst_iterator.__next__()
其中,
from collections.abc import Iterable,Iterator,Generator
isinstance(lst_iterator, Iterator) >> True
解释迭代器(iterator) 和可迭代对象(iterable)
- 首先,在python的类型定义里:
class Iterable(object)
class Iterator(Iterable)
- Iterator 是迭代器,迭代工具,功能是逐次调用
__next__
方法,返回当前值并运行到下一个迭代对象 - Iterable 是可迭代对象,是一个可以返回自身所有值的容器,可以用于
for ... in ...
语法糖或者map
等方法, Iterable通过__iter__
方法返回一个迭代器对象,然后后续迭代运行则利用此迭代器进行,比如上面的for ... in ...
的等价运行
值得注意的是Iterator也要实现__iter__
方法并返回自身,因此Iterator 也是Iterable对象,这样做的好处是让iterator也可以用于for... in...
等语法糖遍历方法(具体原因在for的运行机制里)
class Iterable_cls(object):
def __init__(self):
pass
def __iter__(self):
pass
class Iterator_cls(object):
def __init__(self,):
pass
def __iter__(self):
return self # 返回自身
def __next__(self):
pass
- 举例:
class Iterable_cls(object):
# 是iterable不是iterator
def __init__(self, baselist):
self.baselist = baselist
def __iter__(self):
return iter(self.baselist)
class Iterable_cls(object):
# 是iterable不是iterator
def __init__(self, baselist):
self.baselist = baselist
def __iter__(self):
for i in self.baselist:
yield i
class Iterable_cls(object):
# 是iterator
def __init__(self, baselist):
self.baselist = baselist
self.index=-1
def __iter__(self):
return self
def __next__(self,):
self.index += 1
return self.baselist[self.index]
- 非可迭代对象,但可以进行迭代操作:当类型实现了__getitem__的时候,也可以用于for…in…语法糖,只是迭代以下标为基础,默认从0下标开始;
- 同样,iter()也可以针对这种类型实例返回一个迭代器;但是这种类不是iterable的
class NonIterable(object):
def __init__(self, baselist):
self.baselist = baselist
def __getitem__(self,index):
return self.baselist[index]
for i in NonIterable([1,2,3]): pass # 可运行
iterator_ = iter(NonIterable([1,2,3]))
from collections.abc import Iterable,Iterator,Generator
isinstance(iterator_, Iterator) # True
isinstance(NonIterable([1,2,3], Iterable) # False
generator
- generator的实现有两种,一种是列表表达式
(x for x in <expression>)
, 另一种是使用yield
关键字 - yield 关键字:yield 将函数变成一个生成器函数,generator 必然有__iter__和__next__方法,与上述Iterator的方法执行效果一样
当生成器函数被调用的时候,生成器函数不执行内部的任何代码,直接立即返回一个迭代器。
当所返回的迭代器第一次调用next的时候,生成器函数从头开始执行,如果遇到了执行yield x,next立即返回yield值x。
当所返回的迭代器继续调用next的时候,生成器函数从上次yield语句的下一句开始执行,
直到遇到下一次执行yield任何时候遇到函数结尾,或者return语句,抛出StopIteration异常。
- 启动生成器的一次迭代的方法可以是
<generator>.send()
、<generator>.__next__()
def gen_func():
print('head')
yield 1
print('first')
yield 2
print('second')
gen = gen_func() #
ininstance(gen, Generator) >> True
isinstance(gen, Iterator) >> True
gen.__next__() # 第一次迭代, 函数从头部运行到第一个'yield x 运行结束' 处暂停
>> 'head'
>> 1
gen.__next__() # 第二次迭代, 函数从上一个暂停的地方运行到下一个'yield x 结束'处暂停
>> 'first'
>> 2
gen.__next__() # 第三次迭代, 函数从上一个暂停的地方运行到下一个yield,已经没有下一个元素,所以会抛出StopIteration
>> 'second'
>> StopIteration
- 解释send: 上述函数也可以用send来进行迭代
gen = gen_func1()
gen.send(None)
gen.send(None)
gen.send(None)
结果和next一样
- send(<input_arg>) 做了什么? 在yield x将x值输出到生成器外部后,将input_arg作为 yield x 语句返回值的替代输入到生成器内部;
- 首先,yield x 语句是有返回值None的,因此yield x 是将x输出到外部,并在内部产生一个返回值None:
def gen_func2():
# 生成器函数
x = 1
while True:
y = yield x*2 # 将 yield语句的返回值赋给y
x *= 2
print('y: ',y)
gen = gen_func2()
gen.__next__() # 输出 x*2, 并停止在yield x结束处,具体在何处?下面会解释
>> 2
>> gen.__next__() #
>> 'y: 'None # 可见yield x返回None
>> 4
- 然后,y = yield x具体在一次迭代中暂停的位置,实际上是在yield x 的左侧,停在赋值之前:
y = /<pause>/(yield x*2)
- python 代码的读法和运行是从左到右,但进行赋值语句时,先将=右侧的语句运行完后才会进行赋值,因此y = yield x的迭代过程:
y = (yield x):
1. yield x, Pause # 一次迭代,out x, return None,暂停
2. y = None, <expression>, yield x, Pause # 下一次迭代,赋值,运行到yield x结束暂停
- 所以,send(input_arg)代替了yield x的值,则将 input_arg 赋值给了y
gen = gen_func2()
gen.send(None) # 首次运行,没有到yield x,所以只能是None
gen.send(1)
>> 2
gen.send(1)
>> 'y: '1 # send(value) 替代yield x 的返回值,赋值给了y
>> 4
参考: