Python迭代器是为了解决遍历数据集合的问题而来的。在Python中,可以使用for循环遍历列表、元组、字典等数据集合,但是对于大型数据集合或者需要进行复杂的数据处理时,这种方法可能会导致内存占用过大或者处理速度过慢的问题。 迭代器提供了一种延迟计算数据的方法,只有当需要使用数据时,才会进行计算并返回结果。这种方式可以有效减少内存占用,并且可以提高代码的执行效率。

什么是迭代器?

迭代器是一个可以记住遍历位置的对象,它从集合的第一个元素开始访问,直到所有元素被访问完毕。迭代器只能向前不会后退。由__iter__、__next__实现

二、迭代器示例

python设计迭代器的原因:是为了寻求一种不依赖索引也能够进行迭代取值的方案,所以python给那些没有索引的数据类型都内置了一个功能,叫__iter__功能,如果我们不想或者不能依赖索引进行迭代取值,那我们就可以调用python给我们提供的这个功能就可以了, 只要调用了__iter__方法,那么python就会 将迭代对象转化为迭代器对象 ,有了这个迭代器对象,我们就可以不依赖索引进行迭代取值了,那这个迭代器对象是怎么不依赖于索引进行迭代取值呢?

d = {"key1": 1, "key2": 2, "key3": 3}
res = d.__iter__()  # 将可迭代对象转化为迭代器 
print(res)
print(res.__next__())  # key1
print(res.__next__())  # key2
print(res.__next__())  # key3
print(res.__next__())  # 这时取值已经取完,将抛出StopIteration异常
"""
迭代器节约内存资源,把迭代器比喻成一只老母鸡,这里只能下三只蛋,下完后就死了,不能重新调用生成,如果想再次生成,就自己再新建一只老母鸡。
就是说如果这个老母鸡一辈子可以生1000个鸡蛋,肯定不是它肚子里直接就存着1000个鸡蛋,而是一个一个来的,我想说的是节约内存资源,效率高

while第一次循环就已经取完了老母鸡里的鸡蛋,然后老母鸡就死了,所以第二次循环是取不到值的,直接捕获异常break了。那么我们就需要重新再造一只老母鸡

while True:
    try:
        print(res.__next__())
    except StopIteration:
        break
while True:
    try:
        print(res.__next__())
    except StopIteration:
        break
其他的类型只要有__iter__方法的数据类型---列表、元组、字符串、集合、还有文件,不管有索引还是没有索引,都可以用这种方法进行迭代取值。
# 针对上面使用的while循环,直接使用for循环要简单很多(上面与for循环原理一样)
d = {"key1": 1, "key2": 2, "key3": 3}
for key in d:
    print(key)

迭代器调用iter()方法:

print(res.__iter__())  # res是迭代器,打印迭代器本身
即然调用iter()打印的是迭代器本身,那么这种方式没有意义,为什么要设计出来呢?真的就是没有意义吗?不,这种方式的出现是
为了让for循环的工作原理统一起来,不管for循环的in后面跟的是可迭代对象,还是迭代器对象,都可以采用同一套运行机制,如果我们for i in 一个可迭代对象,它会去调用这个可迭代对象的__iter__方法,把它转换成一个迭代器,如果这里本身放的就是一个迭代器对象,那我for循环的工作原理不变,还是会
去调用它的__iter__(迭代器对象的__iter__),拿到的结果也是一个迭代器

列表、元组、集合、字典都没有__next__方法,所以他们是可迭代对象,文件对象有__iter__、__next__,是迭代器对象

三、for循环原理

1.调用__iter__()方法将可迭代对象转化为迭代器对象

2.while循环不断调用迭代器对象的__next__()方法,直到所有元素都被遍历。

3.在每次调用__next__()方法时,迭代器会返回下一个元素。如果没有更多元素,next()方法会抛出StopIteration异常。

4.for循环捕获StopIteration异常,结束循环。

字典的values()、keys()、items()都是可迭代对象:

print({"key1":123}.items().__iter__().__next__()) # ('key1', 123),先转换成了迭代器,然后取值。实际上这是执行了for循环原理。
# 所以得出for循环可直接遍历:
for k,v in {"key1": 123}.items():   #print({"key1": 123}.items())------>dict_items([('key1', 123)]) 
print(k) # key1
print(v) # 123

list、tuple工作原理

for循环的原理就是这三个步骤,所以说list和tuple的工作原理也是这三步(如,list("hello")),先生成空列表;然后调用符串下面的__iter__方法,转成一个迭代器,然后调用迭代器下面的__next__方法拿到返回值,把返回值放在空列表里面。

四、迭代器与生成器区别

  • 迭代器是一种工具,它可以遍历访问可迭代对象中的所有元素,但是它并不是可迭代对象。
  • 生成器是一种特殊的迭代器,它可以通过函数来实现。生成器函数使用yield语句来返回一个值,每次调用生成器函数时,都会从上一次yield语句处继续执行。因此,生成器可以延迟计算,只有在需要时才会生成下一个元素,从而节省内存。
  • 生成器与迭代器示例:

    # 迭代器示例
    class Counter:
        def __init__(self, n):
            self.n = n
            self.current = 0
        def __iter__(self):
            return self
        def __next__(self):
            if self.current < self.n:
                num = self.current
                self.current += 1
                return num
            else:
                raise StopIteration
    # 生成器示例
    def counter(n):
        current = 0
        while current < n:
            yield current
            current += 1

    # 生成器表达式生成生成器
    gen = (x for x in range(1, 4))
    print(type(gen)) # <class 'generator'>
    print(next(gen)) # Output: 1
    print(next(gen)) # Output: 2
    print(next(gen)) # Output: 3

    计算序列中元素之和:

    # 使用迭代器计算序列中所有元素的和
    def iter_sum(nums):
        total = 0
        it = iter(nums)
        while True:
            try:
                total += next(it)
            except StopIteration:
                break
        return total
    # 使用生成器计算序列中所有元素的和
    def gen_sum(nums):
        total = 0
        for num in nums:
            total += num
        return total