迭代器、可迭代对象、for循环range本质

迭代器

迭代器的定义

1/ 当一个类中 定义了 __iter__ 和 __next__ 两个方法,就是一个迭代器
2/ __iter__ 方法需要返回对象本身,也就是 self
3/ __next__ 方法,返回下一个数据,如果没有数据了,需要抛出一个 StopIteration 的异常

定义一个迭代器

class It():
    def __init__(self) -> None:
        self.count = 0

    def __iter__(self):
        return self
    
    def __next__(self):
        self.count +=1
        if self.count == 5:
            raise StopIteration()
        return self.count

这个类It 就是一个迭代器,而这个类的实例就是一个迭代器对象

obj = It() # obj就是一个迭代器对象

print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__()) 
输出:
1
2
3
4
第五个print 报错 StopIteration()

obj = It()
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
同上

for 循环本质

obj = It()

for item in obj:
    print(item)
输出:
	1  2  3  4

# 1/ for循环会去 执行迭代器对象obj的__iter__方法 并获取返回值【迭代器对象】
# 2/ 一直不停的调用 next(对象) 并赋值给 item

思考一个问题

#修改代码为:
for item in obj:
    print(next(obj))
    print(item)
#输出 什么?

分析:

输出的结果为:
2	1	4	3

1/ for循环先执行obj的__iter__方法  返回的是obj 的self本身
2/ 然后调用next(obj) 获取返回值 1  赋值给item
3/ print(next(obj)) 这里再执行了一次 next(obj) 所以输出 2
4/ 再来输出item  所以输出刚赋值的 1
5/ 再调用next(obj) 获取返回值 3 赋值给item
6/ print(next(obj)) 这里再执行了一次 next(obj) 所以输出 4
7/ 输出刚赋值的item 所以输出 3
8/ 再次尝试调用next(obj)赋值给 item  这时候5 已经触发了StopIteration终止循环
所以输出的是 2 1 4 3

生成器

生成器本质上是一种特殊的迭代器

def func():
    yeild 'aa'
    yeild 'bb'

# 这里底层是用 generator 生成器类创建的一个对象,生成器内部也声明了 __iter__ __next__
obj = func()

print(type(obj)) # <class 'generator'>

print(obj.__next__())  #aa
print(next(obj)) #bb
print(next(obj)) # 抛出StopIteration异常

可迭代对象

注意区分概念:
1/ 迭代器对象: 由迭代器 实例化出来的对象 为迭代器对象
2/ 可迭代对象: 如果一个类 有__iter__方法,且返回了一个 迭代器对象,那么这个类实例化出来的对象就为  可迭代对象

举例:

# 这是一个迭代器,实现了 __iter__ 和 __next__ 两个方法,并且iter返回本身
class It():
    def __init__(self) -> None:
        self.count = 0

    def __iter__(self):
        return self
    
    def __next__(self):
        self.count +=1
        if self.count == 5:
            raise StopIteration()
        return self.count
obj1 = It() ## 这个obj就是一个 迭代器对象

#这个类 并不是迭代器,因为都没有__next__方法,但是这个类有__iter__方法,并且返回的是一个 迭代器对象【It 为迭代器,实例化是迭代器对象】
class Demo:
    def __iter__(self):
        return It()
demo = Demo()  ## 这个demo就是一个  可迭代对象

for i in demo:
    print(i)

# 同理,生成器是一个特殊的迭代器,所以生成器对象也是一个迭代器对象
def func():
    yeild 1
    yeild 2
class Demo2:
    def __iter__(self):
        return func()

for i in demo2:
    print(i)


class T:
    def __iter__(self):
        yield 1
        yield 'xx'
        yield 'haha'


for i in T():
    print(i)

理解练习

for i in range(10):
    print(i)
#这个 range(10)返回的一定是一个 可迭代对象

v1 = range(10)
print(dir(v1))
# 返回的dir 发现只有__iter__, 并没有__next__,所以range()返回的只是一个可迭代对象,并不是迭代器对象

#测试一下
next(v1)  # 会报错  'range' object is not an iterator 说明不是一个迭代器对象

v2 = v1.__iter__() # 可迭代对象的iter方法返回的是一个迭代器对象
print(dir(v2))  #这里就可以看到有__iter__  有 __next__ 方法了

print(v2,type(v2))# <range_iterator object at 0x7f5eac2ae960> <class 'range_iterator'>

v2.__next__()
next(v2)  执行就成功了

需求:

自定义一个range,要求是一个闭区间,比如原来的range(5) 只有 0 1 2 3 4,这里需要输出 0 1 2 3 4 5

class MyIterRange():

    def __init__(self,min,max):
        self.min = min
        self.max = max
        self.strat = 0

    def __iter__(self):
        return self
    
    def __next__(self):
        self.min = self.min+self.strat
        self.strat = 1
        if self.min > self.max:
            raise StopIteration()
        return self.min

class MyRange():
    def __init__(self,min,max):
        self.min = min
        self.max = max
    def __iter__(self):
        return MyIterRange(self.min,self.max)

# 用可迭代对象
for i in MyRange(1,9):
    print(i)

# 直接用迭代器对象,迭代器对象一定是个可迭代对象
for i in MyIterRange(-4,1):
    print(i)

或者用更简单的方法:

class MyYieldRange():
    
    def __init__(self,min,max):
        self.min = min
        self.max = max
    
    def __iter__(self):
        while self.min <= self.max:
            yield self.min
            self.min+=1


for i in MyYieldRange(-1,8):
    print(i)

结论

  • 迭代器 实例化 生成 迭代器对象

  • 生成器是一个特殊的迭代器,实例化出来的也是一个迭代器对象

  • 可迭代对象 不一定是 迭代器对象 ,但是迭代器对象一定是可迭代对象

  • for 循环本质是先去调用可迭代对象的__iter__方法,获取返回的迭代器对象,然后不停的调用迭代器对象的__next__方法 直到StopIteration()

posted @ 2019-11-14 16:35  Alantammm  阅读(34)  评论(0编辑  收藏  举报