Python可迭代对象,迭代器,生成器的区别
Python可迭代对象,迭代器,生成器的区别
三者简要关系图
可迭代对象与迭代器
可迭代对象与迭代器不同点
1)可迭代对象包含迭代器。
2)如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
3)定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和next方法。
你也许会问,结论3与结论2是不是有一点矛盾?既然一个对象拥有了next方法就是迭代器,那为什么迭代器必须同时实现两方法呢?
因为结论1,迭代器也是可迭代对象,因此迭代器必须也实现__iter__方法。
介绍一下上面涉及到的两个方法:
1)iter()
该方法返回的是当前对象的迭代器类的实例。因为可迭代对象与迭代器都要实现这个方法,因此有以下两种写法。
写法一:用于可迭代对象类的写法,返回该可迭代对象的迭代器类的实例。
写法二:用于迭代器类的写法,直接返回self(即自己本身),表示自身即是自己的迭代器。
也许有点晕,没关系,下面会给出两写法的例子,我们结合具体例子看。
2)next()
返回迭代的每一步,实现该方法时注意要最后超出边界要抛出StopIteration异常。
可迭代对象和可迭代器实例
'''这是自定义的可迭代对象实现
Iterable 判断可迭代
Iterator 迭代器
生成器是特殊的迭代器
'''
from collections import Iterable, Iterator
# 这是可迭代对象
class MyList(object):
def __init__(self):
self.items = list()
def append_item(self, obj):
self.items.append(obj)
def __iter__(self):
'''需要返回的是一个迭代器'''
return MyIterator(self.items)
# 这是我的迭代器
# 两个方法记录迭代的位置和记录迭代到的值
class MyIterator: # 定义可迭代器类
def __init__(self, items):
self.items = items # 传入参数
self.current_index = 0 # 当前迭代位置
def __iter__(self):
return self # 返回该可迭代对象的迭代器的实例
def __next__(self): # 迭代器类必须实现的方法
'''在当前的方法里,实现记录位置和值'''
if self.current_index < len(self.items):
value = self.items[self.current_index]
self.current_index += 1
return value
else:
raise StopIteration # 超出位置则抛出异常
mylist = MyList() # 用自己的可迭代对象创建
mylist.append_item(1)
mylist.append_item(2)
mylist.append_item(3)
mylist.append_item(4)
print(isinstance(mylist, Iterable)) # 判断是否可迭代对象
for i in mylist:
print(i)
print(isinstance(mylist, Iterator)) # 判断是否是迭代器
myIterator = iter(mylist)
print(myIterator)
print('############################')
while True: # 这是for in循环的本质,通过next遍历迭代器
try:
ret = next(myIterator)
print(ret)
except StopIteration as e:
break
iter函数
问题:上面的例子中出现了iter函数,这是什么东西?和__iter__方法有关系吗?
其实该函数与迭代是息息相关的,通过在Python命令行中打印“help(iter)”得知其有以下两种用法。
用法一:iter(callable, sentinel)
不停的调用callable,直至其的返回值等于sentinel。其中的callable可以是函数,方法或实现了__call__方法的实例。
用法二:iter(collection)
1)用于返回collection对象的迭代器实例,这里的collection我认为表示的是可迭代对象,即该对象必须实现__iter__方法;事实上iter函数与__iter__方法联系非常紧密,iter()是直接调用该对象的__iter__(),并把__iter__()的返回结果作为自己的返回值,故该用法常被称为“创建迭代器”。2)iter函数可以显示调用,或当执行“for i in obj:”,Python解释器会在第一次迭代时自动调用iter(obj),之后的迭代会调用迭代器的next方法,for语句会自动处理最后抛出的StopIteration异常。
生成器
生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__iter__和next方法),不需要再手动实现两方法。
生成器在迭代的过程中可以改变当前迭代值,而修改普通迭代器的当前迭代值往往会发生异常,影响程序的执行。
具有yield关键字的函数都是生成器,yield可以理解为return,返回后面的值给调用者。不同的是return返回后,函数会释放,而生成器则不会。在直接调用next方法或用for语句进行下一次迭代时,生成器会从yield下一句开始执行,直至遇到下一个yield。
生成器实例
# list_1 = [i for i in range(100)]
gen_2 = (i for i in range(100)) # 生成器保存的是算法
for i in range(101):
print(next(gen_2))
- 函数有了yield之后,函数名+()就变成了生成器 return在生成器中代表生成器的中止,直接报错 next的作用是唤醒并继续执行
send的作用是唤醒并继续执行,发送一个信息到生成器内部
def create_counter(n):
print("create_counter")
while True:
yield n
print("increment n")
n +=1
对yield的总结
(1)通常的for…in…循环中,in后面是一个数组,这个数组就是一个可迭代对象,类似的还有链表,字符串,文件。他可以是a = [1,2,3],也可以是a = [x*x for x in range(3)]。
它的缺点也很明显,就是所有数据都在内存里面,如果有海量的数据,将会非常耗内存。
(2)生成器是可以迭代的,但是只可以读取它一次。因为用的时候才生成,比如a = (x*x for x in range(3))。!!!注意这里是小括号而不是方括号。
(3)生成器(generator)能够迭代的关键是他有next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。
(4)带有yield的函数不再是一个普通的函数,而是一个生成器generator,可用于迭代
(5)yield是一个类似return 的关键字,迭代一次遇到yield的时候就返回yield后面或者右面的值。而且下一次迭代的时候,从上一次迭代遇到的yield后面的代码开始执行
(6)yield就是return返回的一个值,并且记住这个返回的位置。下一次迭代就从这个位置开始。
(7)带有yield的函数不仅仅是只用于for循环,而且可用于某个函数的参数,只要这个函数的参数也允许迭代参数。
(8)send()和next()的区别就在于send可传递参数给yield表达式,这时候传递的参数就会作为yield表达式的值,而yield的参数是返回给调用者的值,也就是说send可以强行修改上一个yield表达式值。
(9)send()和next()都有返回值,他们的返回值是当前迭代遇到的yield的时候,yield后面表达式的值,其实就是当前迭代yield后面的参数。
(10)第一次调用时候必须先next()或send(),否则会报错,send后之所以为None是因为这时候没有上一个yield,所以也可以认为next()等同于send(None)