迭代器
- 什么是迭代器?迭代器对象就是对象支持迭代器协议换而言之就是:实现对象的__iter__() 和 __next__() 方法。
- 其中__iter__()返回对象本身
- __next__()返回容器中的下一个元素
- 可迭代对象与迭代器对象有什么区别
- 可迭代对象是指所有可以迭代的对象
- 迭代器对象就是通过惰性机制通过next()方法调用元素的对象。
自定义迭代器对象
class MyRange(object):
def __init__(self, n):
self.idx = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.idx < self.n:
val = self.idx
self.idx += 1
return val
else:
raise StopIteration()
输出结果
>>> for i in MyRange(3):
... print(i)
...
0
1
2
解决迭代器对象没有办法多次迭代的问题
class Frange(object):
def __init__(self, n):
self.n = n
def __iter__(self):
return SubRange(self.n)
class SubRange(object):
def __init__(self, n):
self.n = n
self.id = 0
def __iter__(self):
return self
def __next__(self):
if self.id < self.n:
val = self.id
self.id += 1
return val
else:
raise StopIteration()
输出结果如下
>>> for i in Frange(3):
... print(i)
...
0
1
2
>>> for i in Frange(3):
... print(i)
...
0
1
2
生成器
- 在Python中,使用生成器可以很方便的支持迭代器协议。生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。
- 也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。
弄明白生成器动作原理
def zrange(n):
print("生成器函数开始工作")
i = 0
while i < n:
print("yield 执行之前", i)
yield i
i += 1
print("yield 执行之后",i)
print("生成器函数执行完毕")
输出如下
>>> zranged = zrange(3)
>>> for i in zranged:
... print(i)
...
生成器函数开始工作
yield 执行之前 0
0
yield 执行之后 1
yield 执行之前 1
1
yield 执行之后 2
yield 执行之前 2
2
yield 执行之后 3
生成器函数执行完毕
生成器表达式
>>> make_reg = (x for x in range(10))# 此处就是生成器表达式,语法规则和列表推导式特别相似
>>> for i in make_reg:
... print(i)
...
0
1
2
3
4
5
6
7
8
9
>>> for i in make_reg:
... print(i)
...
递归生成器
def permutations(li):
if len(li) == 0:
yield li [012]
else:
for i in range(len(li)):
li[0], li[i] = li[i], li[0]
for item in permutations(li[1:]):
yield [li[0]] + item
for item in permutations(range(3)):
print(item)
生成器的send()和close()
- 调用send()方法相当于给yield传递了一个参数,同时执行了__next__()方法
- 调用close()用于关闭生成器当执行此方法之后再调用send() 或 __next__()会有异常
使用send 需注意地方,在第一次调用生成器的时候需要先调用__next__()或者__send__(None),因为在第一次调用时假如send传递非None参数此时没有yield参数来接收参数,也就是说send传递非空参数时生成器必须处于挂起状态。
def zrange(n):
i = 0
while i < n:
val = yield i
print("val is ",val)
i += 1
>>> zr = zrange(5)
>>> print(zr.send())# 第一次调用send()此时会报错
##############结果报错如下
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: send() takes exactly one argument (0 given)
#############可见send必须传入一个参数而且不能是空的
>>> print(zr.send(8))#此时传入一个非空参数
############执行结果
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
############
>>> print(zr.__next__())
0
>>> print(zr.send(9))
val is 9
1
生成器迭代器总结
- 通过实现迭代器协议对应的__iter__()和next()方法,可以自定义迭代器类型。对于可迭代对象,for语句可以通过iter()方法获取迭代器,并且通过next()方法获得容器的下一个元素
- 像列表这种序列类型的对象,可迭代对象和迭代器对象是相互独立存在的,在迭代的过程中各个迭代器相互独立;但是,有的可迭代对象本身又是迭代器对象,那么迭代器就没法独立使用。
- itertools模块提供了一系列迭代器,能够帮助用户轻松地使用排列、组合、笛卡尔积或其他组合结构
- 生成器是一种特殊的迭代器,内部支持了生成器协议,不需要明确定义__iter__()和next()方法
- 生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果