Python小知识:迭代器 vs 可迭代对象 vs 生成器
Posted 朝阳区靓仔_James
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python小知识:迭代器 vs 可迭代对象 vs 生成器相关的知识,希望对你有一定的参考价值。
先上结论
先说结论,再解释:
- iterable是可迭代对象,它的唯一特征是有__iter__函数,调用这个函数会返回一个iterator。
- iterator是迭代器,它的唯一特征是有__next__函数,调用这个函数会返回下一个元素。
- 有些类同时有以上两个函数,所以即是iterable,又是iterator,这是为了方便,不用额外创建iterator类。
- generator是用yield函数定义的iterator。它必然也有__next__函数。
几个iterable例子
可以用for循环遍历的对象都是可迭代对象iterable,比如list, range, tuple等:
nums = [9527, 3721, 56, 97]
for n in nums:
print(n)
for n in range(1, 100):
print(n)
seasons = ('春', '夏','秋','冬')
for s in seasons:
print(s)
iterable还有很多,它们的唯一必要特征是:
iterable必须有__iter__函数,这个函数的作用就是返回迭代器iterator。
请默默把这句话重复3遍,记住它!
我们来验证一下这个特征,用dir函数看看上面3个iterable有没有__iter__函数:
再说一遍,__iter__函数是iterable的唯一特征。
注意,这些对象并没有__next函数,因为next__是iterator的特征。上面3个都不是iterator。
设计模式和原理
迭代器是一个通用的设计模式,不同编程语言都有各自的实现。
- 我们把list,tuple,和range等iterable当做是一个仓库,里面存放着可以被取用的东西。它们的内部结构各不相同。
- 为了能够用for循环遍历仓库中的东西,我们给这些iterable都配上一个仓库管理员,那就是iterator。for只要找仓库管理员就可以了,而不需要知道里面具体如何存放的。这样问题就简单了。
- __iter__函数是从仓库中获得管理员的函数。一个对象只要有这个函数,就相当于配备了仓库管理员,就是可迭代对象,就是iterable。
- 现在看iterator,它的唯一特征就是有__next__函数,for循环每次调用这个函数获得下一个元素,直到出现StopIteration异常为止。
整个过程就是这样的:
通过这个设计模式,for循环不管被迭代的对象是什么牛鬼蛇神(正方形,圆,对话框,圆角正方形等等),它只找管理员:
-
调用iterable的__iter__函数,获得iterator对象。
注意:实际上for是调用Python内置的iter()函数,而这个函数又调用了iterable的__iter__函数。
-
调用iterator的__next__函数,获取下一个元素,直到获得StopIteration为止。
注意:实际上for是调用Python内置的next()函数,而这个函数又调用了iterator的__next__函数。
我们来验证一下:
从图中可以看出:
- a是一个列表
- 调用iter(a)返回的是一个list_iterator对象
- 这个对象有__next__函数
到这里你应该对iterator和iterable有了比较好的了解。这里涉及到两个类:
- 一个对象是仓库,也就是iterable。
- 一个对象是仓管员,也就是iterator。
定义自己的Iterator和Iterable
理解了这个原理,我们可以轻松创建一个可迭代对象以及它的迭代器。下面我们来创建一个随机列表:RandList。它的特点是:随机遍历访问一个列表中的内容。
先定义一个iterator:
import random
# 这是一个iterator
class RandListIterator():
def __init__(self, rand_list):
# 复制一份列表,防止影响原列表
self.list = rand_list[:]
# 每次遍历随机选择一个,并删除已经返回的元素,直到列表为空
def __next__(self):
if len(self.list) == 0:
raise StopIteration
index = random.randint(0, len(self.list)-1)
value = self.list[index]
del self.list[index]
return value
在定义一个iterable,它使用前面定义的iterator:
# 这是一个iterable
class RandList():
def __init__(self, alist):
self.list = alist
def __iter__(self):
return RandListIterator(self.list)
现在用for循环来循环一下:
alist = [4, 6, 3, 8, 23, 1]
for i in RandList(alist):
print(i)
可以随机的访问列表中的内容:
一个类既是iterable,也是iterator
上面的例子,我们定义了两个类。很多时候为了方便,我们会让一个类把两件事情都做了,毕竟只要这个类同时有__iter和next__函数就可以了。
我们现在定一个随机数生成器,我把它命名为Randable,它的功能是:随机生成10个1到100之间的随机数。
import random
class Randable():
def __init__(self):
self.count = 0
def __iter__(self):
return self
def __next__(self):
if self.count == 10:
raise StopIteration
rand_num = random.randint(1, 100)
self.count += 1
return rand_num
这个类同时包含了__iter和next__两个函数,自己是仓库,又是仓库管理员。就是为了代码方便点,毕竟确实没必要写到两个类里。
使用上面的Randable类:
for i in Randable():
print(i)
执行结果:
到这里,你对iterable和iterator的理解应该很透彻了吧。
推荐阅读
以上是关于Python小知识:迭代器 vs 可迭代对象 vs 生成器的主要内容,如果未能解决你的问题,请参考以下文章