一、迭代
1、定义
迭代是指通过for循环遍历对象每一个元素的过程。
二、可迭代对象(iterable)
1、定义
可迭代对象是定义了 __iter__()
方法或 __getitem__()
方法的类对象。
2、__iter__()
from collections import abc
class MyIterable:
def __iter__(self):
pass
my_iterable = MyIterable()
3、__getitem__()
from collections import abc
class MyIterable:
def __getitem__(self, item):
pass
my_iterable = MyIterable()
定义了__iter__()
或__getitem__()
方法后,对象就可以使用for循环遍历了。当然,上面只是一个简单的示例,什么功能都没有,实际情况一般基于Python的内置数据类型(int, list, string…)去写。在引入迭代器之后,__iter__()
的功能一般是返回一个迭代器。
4、说明
很多文章喜欢拿 collections.abc.Iterable
对象和一个类对象比较,然后判断这个类对象是否是可迭代对象。如:
from collections import abc
class MyIterable:
def __getitem__(self, item):
pass
my_iterable = MyIterable()
print(isinstance(my_iterable, abc.Iterable))
其实这是不完全正确的。因为根据Python文档, Iterable 只实现了__iter__()
方法,所以一个对象如果实现 了__getitem__()
方法,但是没有实现__iter__()
方法,用这种方式判断,该对象就会被判定为不是可迭代对象,这是错误的。只要实现这两个方法中的任何一个,都是可迭代对象,应该使用**iter()**方法。
三、迭代器
1、定义
实现了迭代器协议(__iter__()
, __next__()
)的类对象称为迭代器。迭代器是在Python 2.2引入的,因为迭代器实现了__iter__()
方法,所以迭代器也属于可迭代对象。
2、__iter__()
返回对象本身。
3、__next__()
返回对象的下一个元素,如果没有下一个元素则抛出 StopIteration
异常。
# 模仿range()迭代器自定义一个迭代器
class MyRangeIterator:
def __init__(self, num):
self.num = num
self.index = 0 # 设置第一个元素
def __iter__(self):
return self
def __next__(self):
if self.index < self.num:
val = self.index
self.index += 1 # 为了每次返回下一个元素,所以每次+1
return val
else:
raise StopIteration() # 如果没有元素了则抛出StopIteration
mri = MyRangeIterator(5)
print([i for i in mri]) # [0, 1, 2, 3, 4]
print([i for i in mri]) # []
4、典型的可迭代对象与迭代器
典型的可迭代对象的 __iter__()
方法返一个新的迭代器实例,而迭代器的__iter__()
方法返回迭代器本身。
# 可迭代对象
class MyRange:
def __init__(self, num):
self.num = num
def __iter__(self):
return MyRangeIterator(self.num)
# 迭代器
class MyRangeIterator:
def __init__(self, num):
self.num = num
self.index = 0 # 设置第一个元素
def __iter__(self):
return self
def __next__(self):
if self.index < self.num:
val = self.index
self.index += 1 # 为了每次返回下一个元素,所以每次+1
return val
else:
raise StopIteration() # 如果没有元素了则抛出StopIteration
mr = MyRange(5)
print([i for i in mr]) # [0, 1, 2, 3, 4]
print([i for i in mr]) # [0, 1, 2, 3, 4]
四、迭代原理
解释器需要迭代对象 x 时,每迭代一次:
(1)执行 iter(x)。
(2)检查对象是否实现了__iter__
方法,如果实现了就调用它,获取到一个迭代器iterator。
(3)获取到迭代器之后,执行next(iterator)
。
(4)然后执行 iterator.__next__()
获取元素。
(5)如果没有元素了就抛出 StopIteration
。
五、生成器
1、定义
根据Python术语表,有:
(1)generator
包含 yield
表达式的函数或者生成器叫做生成器。生成器本质上是一个函数,也叫生成器函数(generator function)。
示例:
def gen_123():
yield 123
print(gen_123) #<function gen_123 at 0x00000269E04D6558>,生成器本质上是一个函数
(2)generator iterator
生成器生成的对象(generator object,简称生成器对象)称为“generator iterator”。为什么这样命名呢?因为生成器对象也是一个迭代器。
示例:
from collections import abc
def gen_123():
yield 123
print(gen_123) # <function gen_123 at 0x00000269E04D6558>
g = gen_123()
print(g) # <generator object gen_123 at 0x00000269E0446C48>,生成器对象
print(isinstance(g, abc.Iterator)) # True,生成器生成的对象也是一个迭代器
下面是个人理解(不一定正确):
a. generator iterator
这个名称有点“怪”,咋一看,你不知道它所表达的意思到底是生成器还是迭代器,还有点拗口,可能官方想表达的是生成器生成的迭代器
。
b.有人喜欢把生成器称为“特殊的迭代器”,其实这是不对的,生成器本质是函数,而迭代器本质是类对象。生成器生成的对象才是迭代器。
2、yield
关于yield的实现原理,可以参考这篇文章Python yield。本文只说yield的用法。
yield表达式 的语法如下:
yield_atom ::= "(" yield_expression ")"
yield_expression ::= "yield" [expression_list | "from" expression]
yield关键字的功能:
(1)返回给调用者一个值,并挂起当前状态。
(2)当执行next(generator iterator
),从离开的地方继续。
3、示例
class MyRange:
def __init__(self, num):
self.num = num
self.index = 0
def __iter__(self):
return self.my_range_generator()
def my_range_generator(self):
while self.index < self.num:
yield self.index
self.index += 1
my_range = MyRange(5)
for i in my_range:
print(i, end=' ') # 0 1 2 3 4
4、生成器表达式
生成器表达式的作用和生成器函数的作用也是一样的,生成一个generator iterator
。所以也是一种生成器。
(1)语法
generator_expression ::= "(" expression comp_for ")"
(2)示例
g = (x*y for x in range(10) for y in range(x, x+10))
参考资料
[1]PEP234, iterator: https://www.python.org/dev/peps/pep-0234/
[2]PEP255, Simple Generators: https://www.python.org/dev/peps/pep-0255/
[3]PEP342, Coroutines via Enhanced Generators:https://www.python.org/dev/peps/pep-0342/