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)