迭代器与生成器


迭代器

定义

  1. 当类中定义了__iter__和__next__两个方法
  2. __iter__方法需要返回对象本身,即:self
  3. __next__方法,返回下一个数据,如果没有数据了,则需要抛出一个StopIteration的异常。

迭代器类型

#创建迭代器类型
class It():
    def __init__(self):
        self.counter = 0
    def __iter__(self):
        return self
    def __next__(self):
        self.counter += 1
        if self.counter == 3:
            raise StopIteration
        return self.counter

#根据类实例化创建一个迭代器对象
print('创建一个迭代器对象obj1')
obj1 = It()

v1 = next(obj1) # obj1.__next__()
print(v1) # 1
v2 = next(obj1) # obj1.__next__()
print(v2) # 2
#v3 = next(obj1) # obj1.__next__()
#print(v3) # raise StopIteration

print('创建一个迭代器对象obj2')
obj2 = It()
for item in obj2: # 首先执行迭代器对象的__iter__方法获取返回值,一直去反复的执行next(对象),并赋值给item
    print(item)

'''
迭代器对象支持通过next取值,如果取值结束则自动抛出StopIteration
for循环内部在循环时,先执行__iter__方法,获取一个迭代器对象,然后不断执行的next取值(有异常StopIteration则终止循环)。
'''

可迭代对象

定义

  1. 如果一个类中有__iter__方法且返回一个迭代器对象,则我们称以这个==类创建的对象==为==可迭代对象==
  2. 可迭代对象是可以使用for来进行循坏,在循环内部其实是先执行__iter__方法,获取其迭代器对象,然后再在内部执行这个迭代器对象的next功能,逐步取值。
# 如果一个类中有__iter__方法且返回一个迭代器对象,则我们称以这个类创建的对象为可迭代对象
class Foo():
    def __iter__(self):
        return 迭代器对象或者是生成器对象

obj = Foo() # obj是可迭代对象

# 可迭代对象是可以使用for来进行循坏,在循环内部其实是先执行__iter__方法,获取其迭代器对象,然后再在内部执行这个迭代器对象的next功能,逐步取值。
for item in obj:
    pass

一般我们是把可迭代对象和迭代器结合来做的:

#迭代器类 他的实例对象是迭代器对象
class It():
    def __init__(self):
        self.counter = 0
    def __iter__(self):
        return self
    def __next__(self):
        self.counter += 1
        if self.counter ==3:
            raise StopIteration()
        return self.counter

#可迭代类 他的实例对象是一个迭代器对象(也就是返回上面的迭代器类)
class Foo():
    def __iter__(self):
        return It()

obj1 = Foo()
# for循环都是先调用obj1这个对象的__iter__方法获取一个迭代器对象(It()),然后去执行他的__next__方法
for item in obj1:
    print(item)

'''所以,如果直接用for去循环一个迭代器对象,那么
for循环都是先调用迭代器这个对象的__iter__方法获取自己,也就是获取到了迭代器对象,然后去执行自己的__next__方法

例如这样:
obj2 = It()
for item in obj2:
    print(item)
'''

实例:range()

  • 过程分析
#range()返回的是一个可迭代对象
>>> v1 = range(1000)
#验证这个可迭代对象,查看他的有__iter__方法,没有__next__方法
>>> dir(v1)
['__bool__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index', 'start', 'step', 'stop']
#可迭代对象调用__iter__函数来获取迭代器对象
>>> v2 = v1.__iter__()
#验证这个迭代器对象,查看他的有__iter__方法,也有__next__方法
>>> dir(v2)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
#然后通过迭代器对象的__next__方法可以获取值
>>> v2.__next__()
0
>>> v2.__next__()
1
>>> v2.__next__()
2
>>> v2.__next__()
3
>>> v2.__next__()
4
#一般我们使用for循环来调用range(),其实也是这样一个过程,首先通过调用__iter__函数来获取迭代器对象,然后通过迭代器对象的__next__方法可以获取值
  • 自己实现range
#迭代器类
class IterRange():
    def __init__(self,num):
        self.num = num
        self.counter = -1
    def __iter__(self):
        return self
    def __next__(self):
        self.counter += 1
        if self.counter == self.num:
            raise StopIteration()
        return self.counter

#可迭代类
class Myrange():
    def __init__(self,max_num):
        self.max_num = max_num

    def __iter__(self):
        return IterRange(self.max_num)

# Myrange(3) 返回的是一个实例对象:可迭代对象
for item in Myrange(3):
    print(item)

生成器

定义

如果按照迭代器的规定来看,其实生成器类也是一种特殊的迭代器类(==生成器也是一个特殊的迭代器==)

#创建生成器函数
def func():
    '''函数中,只要有yield,那么返回就是一个yield_obj生成器(==generator==)对象,不会执行函数内部的代码'''
    yield 1
    yield 2

#创建生成器对象(内部是根据生成器类generator创建的对象),生成器类的内部声明了:__ietr__,__next__方法。 所以也符合迭代器的定义,所以生成器是特殊的迭代器
print('创建生成器对象obj1')
obj1 = func()

v1 = next(obj1)
print(v1) # 1
v2 = next(obj1)
print(v2) # 2
# v3 = next(obj1) # StopIteration
# print(v3)

print('创建生成器对象obj2')
obj2 = func()
for item in obj2:
    print(item)

实例:range()

#基于可迭代对象&生成器 实现:自定义range

class Myrange():
    def __init__(self,max_num):
        self.max_num = max_num
    def __iter__(self):
        counter = 0
        while counter < self.max_num:
            yield counter
            counter += 1

obj = Myrange(3)
for item in obj:
    print(item)

总结

可迭代对象 含==iter==方法,且该方法返回一个迭代器 迭代器对象 含==iter==方法和==next==方法,其中 iter 方法返回对象自身(self), next 方法返回迭代的数据,没有数据时需要抛出 StopIteration 异常,以终止迭代 生成器 函数中只要有==yield==,那么返回就是一个生成器(==generator==)对象,生成器类的内部声明了:ietr,next方法。生成器实际是一种特殊的迭代器

#迭代器
from collections.abc import Iterator
#可迭代对象
from collections.abc import Iterable

a = [1,2,3] # a是可迭代对象:只有__iter__,没有__next__
print('a是迭代器吗',isinstance(a,Iterator))
print('a是可迭代对象吗',isinstance(a,Iterable))

b = a.__iter__() #通过调用一个可迭代对象的__iter__方法,得到的是一个迭代器b:有__iter__,也有__next__
print('b是迭代器吗',isinstance(b,Iterator))
print('b是可迭代对象吗',isinstance(b,Iterable))

#执行结果:
'''
a是迭代器吗 False
a是可迭代对象吗 True
b是迭代器吗 True
b是可迭代对象吗 True
'''

#所以,判断一个对象是否是 可迭代对象,使用 isinstance(obj,Iterator) == False 和 isinstance(obj,Iterable) == True

#判断一个对象是否是 迭代器 使用 isinstance(obj,Iterator) == True