迭代器与生成器
迭代器
可迭代对象:在类里面定义__iter__方法,并使用该类创建的对象就是可迭代对象。
判断是否为可迭代对象
from collections import Iterable print(isinstance("ronie", Iterable)) print(isinstance(['r', 'o', 'n', 'i', 'e'], Iterable)) print(isinstance({'r', 'o', 'n', 'i', 'e'}, Iterable)) print(isinstance(('r', 'o', 'n', 'i', 'e'), Iterable)) print(isinstance({'r': 0, "o": 1, "n": 2, "i": 3, "e": 3}, Iterable)) print(isinstance(0, Iterable)) print(isinstance(True, Iterable)) True True True True True False False
自定义可迭代对象
from collections.abc import Iterable class A: def __iter__(self): pass # 应当返回一个迭代器 a = A() print(isinstance(a, Iterable)) # True
迭代器:一个类如果实现了迭代器协议,就可以称之为迭代器。在Python中,实现迭代器协议就是实现以下2个方法:
- __iter__:这个方法返回对象本身,即self;iter函数获取可迭代对象的迭代器,会调用可迭代对象身上的__iter__方法。
- __next__:这个方法每次返回迭代的值,在没有可迭代元素时,抛出StopIteration异常。next函数会获取迭代器中下一个值,会调用迭代器对象身上的__next__方法。
class MyList(object): def __init__(self): self.my_list = list() self.index = 0 def append_item(self, item): self.my_list.append(item) def __iter__(self): return self def __next__(self): if self.index < len(self.my_list): self.index += 1 return self.my_list[self.index - 1] else: raise StopIteration m = MyList() m.append_item(10) m.append_item(30) m.append_item(20) i = iter(m) print(next(i)) print(next(i)) print(next(i)) # 10 # 30 # 20
for循环:
- for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
- for item in Iterator 循环的迭代器,不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
生成器
创建生成器
生成器表达式:
x = (i for i in range(3)) print(type(x)) print(next(x)) # <class 'generator'> # 0
yield关键字:
def func(): f = [1, 1] while True: f.append(f[-1] + f[-2]) yield f[-1] f = func() print(type(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) # <class 'generator'> # 2 # 3 # 5 # 8 # 13
协程
协程,又称微线程,纤程,也称为用户级线程,在不开辟线程的基础上完成多任务,也就是在单线程的情况下完成多任务,多个任务按照一定顺序交替执行。
yield
协程的简单例子
# 协程计算平均值 def avg(): average = 0 nums = 0 while True: x = yield average average = (average * nums + x) / (nums + 1) nums += 1 a = avg() print(a.send(None)) print(a.send(20)) print(a.send(30)) print(a.send(10)) print(a.send(2)) # 0 # 20.0 # 25.0 # 20.0 # 15.5
- 协程使用生成器函数定义:定义体中有yield关键字。
- yield在表达式中使用;如果协程只需从客户那里接收数据,那么产出的值是None——这个值是隐式指定的,因为yield关键字右边没有表达式。
- 与创建生成器的方式一样,调用函数得到生成器对象。
- 首先要调用next()函数,因为生成器还没启动,没在yield语句处暂停,所以一开始无法发送数据。
预激活协程的装饰器
def activate(func): def f(*args, **kwargs): d = func(*args, **kwargs) next(d) return d return f @activate def avg(): average = 0 nums = 0 while True: x = yield average average = (average * nums + x) / (nums + 1) nums += 1 a = avg() # print(a.send(None)) print(a.send(20)) print(a.send(30)) print(a.send(10)) print(a.send(2))
终止协程和异常处理
发送哨符值,让协程退出:由于未处理的异常会导致协程终止,可以选择发送某个会导致协程异常的值让协程退出。
>>> def activate(func): ... def f(*args, **kwargs): ... d = func(*args, **kwargs) ... next(d) ... return d ... return f ... >>> >>> @activate ... def avg(): ... average = 0 ... nums = 0 ... while True: ... x = yield average ... average = (average * nums + x) / (nums + 1) ... nums += 1 ... >>> a = avg() >>> a.send(20) 20.0 >>> a.send(30) 25.0 >>> a.send(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 7, in avg TypeError: unsupported operand type(s) for +: 'float' and 'NoneType' >>> a.send(40) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
- 使用@coroutine装饰器装饰的averager协程,可以立即开始发送值。
- 发送的值不是数字,导致协程内部有异常抛出。
- 由于在协程内没有处理异常,协程会终止。如果试图重新激活协程,会抛出StopIteration异常。
显式的抛出异常:
>>> def activate(func): ... def f(*args, **kwargs): ... d = func(*args, **kwargs) ... next(d) ... return d ... return f ... >>> >>> @activate ... def avg(): ... average = 0 ... nums = 0 ... while True: ... try: ... x = yield average ... average = (average * nums + x) / (nums + 1) ... nums += 1 ... except Stop: ... pass ... >>> >>> class Stop(Exception): ... pass ... >>> >>> a = avg() >>> # print(a.send(None)) >>> print(a.send(20)) 20.0 >>> print(a.send(30)) 25.0 >>> print(a.send(10)) 20.0 >>> print(a.send(2)) 15.5 >>> a.throw(Stop) 15.5 >>> a.send(10) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
- 如果显示抛入的异常没有处理,则协程会中止。
- 可以调用.close()方法关闭协程。
协程返回值
>>> def activate(func): ... def f(*args, **kwargs): ... d = func(*args, **kwargs) ... next(d) ... return d ... return f ... >>> >>> @activate ... def avg(): ... average = 0 ... nums = 0 ... while True: ... x = yield average ... if x is None: ... break ... average = (average * nums + x) / (nums + 1) ... nums += 1 ... return average ... >>> >>> class Stop(Exception): ... pass ... >>> >>> a = avg() >>> # print(a.send(None)) >>> print(a.send(20)) 20.0 >>> print(a.send(30)) 25.0 >>> print(a.send(10)) 20.0 >>> print(a.send(2)) 15.5 >>> try: ... a.send(None) ... except StopIteration as e: ... print(e.value) ... 15.5 >>>
- 使用return中止一个协程,并返回值。
return中止协程会导致StopIteration异常。
>>> a.send(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration: 15.5
- 异常对象的value属性保存着返回的值。
- 使用try,catch语句捕获异常并获取返回值。
yield from
class MyList(object):
def __init__(self):
self.my_list = list()
self.index = 0
def append_item(self, item):
self.my_list.append(item)
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.my_list):
self.index += 1
return self.my_list[self.index - 1]
else:
raise StopIteration
def func(iterable):
yield from iterable
m = MyList()
m.append_item(23)
m.append_item(12)
m.append_item(54)
print(list(func(m)))
# [23, 12, 54]
- 迭代器(即可指子生成器)产生的值直接返还给调用者
- 任何使用send()方法发给委派生产器(即外部生产器)的值被直接传递给迭代器。如果send值是None,则调用迭代器next()方法;如果不为None,则调用迭代器的send()方法。如果对迭代器的调用产生StopIteration异常,委派生产器恢复继续执行yield from后面的语句;若迭代器产生其他任何异常,则都传递给委派生产器。
- 子生成器可能只是一个迭代器,并不是一个作为协程的生成器,所以它不支持.throw()和.close()方法,即可能会产生AttributeError 异常。
- 除了GeneratorExit 异常外的其他抛给委派生产器的异常,将会被传递到迭代器的throw()方法。如果迭代器throw()调用产生了StopIteration异常,委派生产器恢复并继续执行,其他异常则传递给委派生产器。
- 如果GeneratorExit异常被抛给委派生产器,或者委派生产器的close()方法被调用,如果迭代器有close()的话也将被调用。如果close()调用产生异常,异常将传递给委派生产器。否则,委派生产器将抛出GeneratorExit 异常。
- 当迭代器结束并抛出异常时,yield from表达式的值是其StopIteration 异常中的第一个参数。
- 一个生成器中的return expr语句将会从生成器退出并抛出 StopIteration(expr)异常。
Comments | NOTHING