一文理解python迭代器和生成器

一、python迭代器是什么

1、迭代是什么意思
汉语中,迭代的意思就是更替、轮换 。我们这里说的迭代就是 轮替的访问集合元素的一种方式,专业点就是遍历元素。

2、可迭代对象 (Iterable)
先说定义和怎么判断是不是可迭代对象
如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象 可以通过isinstance方法来检测,列表、元组、字典、字符串和集合等都是可迭代对象。

from collections.abc import Iterable  

class MyIterator:  
    def __init__(self, data):  
        self.index = 0  
        self.data = data  

    def __iter__(self):  
        pass  
a=MyIterator(3)  

print("a 是否是可以迭代对象", isinstance(a, Iterable))  
#打印结果实 a 是否是可以迭代对象 True

python中我们最常用的就是 通过 for…in…的形式进行遍历元素,如果这个元素可以通过这个方式遍历,那么这个元素的数据类型就是可以迭代的,我们也把这个数据类型的数据 叫做可迭代对象。

当我们调用__iter__ 这个方法的时候会有下面的步骤. 总之目的就是为了创建一个迭代器。

  • 检查对象是否实现了 iter 方法,如果实现了就调用它,获取一个迭代器
  • 如果没有实现 iter 方法,但是实现了 getitem 方法,Python 会创建一个迭代
    器,尝试按顺序(从索引 0 开始)获取元素。
  • 如果尝试失败,Python 抛出 TypeError 异常,通常会提示“C object is not iterable”(C
    对象不可迭代),其中 C 是目标对象所属的类。

3、迭代器(Iterator)
先说定义和怎么判断是不是迭代器
如果一个对象同时实现了__iter__方法和__next__方法,它就是迭代器

from collections.abc import Iterable  
from collections.abc import Iterator  


class MyIterator:  
    def __init__(self, data):  
        self.index = 0  
        self.data = data  

    def __iter__(self):  
        pass  

    def __next__(self):  
        pass  


a = MyIterator(3)  

print("a 是否是可以迭代对象:", isinstance(a, Iterable))  #  输出结果是:a 是否是可以迭代对象: True  
print("a 是否是可以迭代器: ", isinstance(a, Iterator))  #   输出结果是:a 是否是可以迭代器:  True

4、可迭代对象和迭代器的关系是什么

可迭代的对象和迭代器之间的逻辑的关系:Python 从可迭代的对象中获取迭代器。
迭代器一定是可迭代对象,反之则不成立,可迭代对象的__iter__方法必须返回一个迭代器

# 创建一个可迭代对象(列表)  
my_list = [1, 2, 3, 4, 5]  
  
# 使用iter()函数从可迭代对象中获取迭代器  
my_iterator = iter(my_list)  
  
# 使用迭代器的__next__()方法获取元素  
print(next(my_iterator))  # 输出:1  
print(next(my_iterator))  # 输出:2  
print(next(my_iterator))  # 输出:3  
# ... 可以继续调用next()直到引发StopIteration异常

既然说,Python 从可迭代的对象中获取迭代器,那就是说,iter调用可迭代对象,生成的对象会有next这个方法。

那这个next是怎么加上去的呢,可以得出,iter这个方法下,return的 是一个包含next命令的对象。很多时候,iter返回迭代器自身,即return self (这种情况,对象本身就是一个装饰器)或者return 一个新的迭代器

在这里插入图片描述

5、当iter返回的是迭代器自身的话(iter函数里 是 return self)

  • 在这个例子中,SimpleIterator 类实现了迭代器协议。当我们创建一个 SimpleIterator 对象并尝试迭代它时,for 循环首先调用 iter 方法来获取迭代器。由于 iter 方法返回迭代器自身,因此 for 循环能够使用 next 方法来逐个访问元素。但是,一旦所有元素都被访问过,再次尝试迭代将不会输出任何内容,因为迭代器已经耗尽。
class SimpleIterator:  
    def __init__(self, data):  
        self.data = data  
        self.index = 0  
  
    def __iter__(self):  
        # 返回迭代器自身  
        return self  
  
    def __next__(self):  
        if self.index < len(self.data):  
            result = self.data[self.index]  
            self.index += 1  
            return result  
        else:  
            raise StopIteration  
  
# 使用迭代器  
it = SimpleIterator([1, 2, 3])  
  
# 第一次迭代  
for item in it:  
    print(item)  # 输出: 1 2 3  
  
# 尝试第二次迭代(将会失败,因为迭代器已经耗尽)  
for item in it:  
    print(item)  # 不输出任何内容,因为迭代器已经耗尽

6、当iter返回的是一个新的迭代器

  • 在这个例子中,MyCollection 是一个可迭代对象,它有一个 iter 方法,该方法返回一个新的 MyCollectionIterator 实例。每次对 my_collection 进行迭代时,都会创建一个新的迭代器,因此可以多次迭代同一个 MyCollection 对象而不会受到之前迭代状态的影响。

  • 而 MyCollectionIterator 类本身就是一个迭代器,它的 iter 方法返回自身。这是因为迭代器对象本身就是用于迭代的,不需要创建额外的迭代器对象。

  • 总结来说,iter 方法返回的对象必须实现迭代器协议,即必须有一个 next 方法,并且当迭代结束时必须抛出 StopIteration 异常。对于迭代器类本身来说,它通常返回自身作为迭代器;而对于可迭代对象来说,它可能会返回一个新的迭代器对象来封装对内部数据的访问。

class MyCollection:  
    def __init__(self, data):  
        self.data = data  
  
    def __iter__(self):  
        # 创建一个新的迭代器对象,封装对 self.data 的访问  
        return MyCollectionIterator(self.data)  
  
class MyCollectionIterator:  
    def __init__(self, data):  
        self.data = data  
        self.index = 0  
  
    def __iter__(self):  
        # 迭代器返回自身  
        return self  
  
    def __next__(self):  
        if self.index < len(self.data):  
            result = self.data[self.index]  
            self.index += 1  
            return result  
        else:  
            raise StopIteration  
  
# 使用可迭代对象  
my_collection = MyCollection([1, 2, 3])  
  
# 第一次迭代  
for item in my_collection:  
    print(item)  # 输出: 1 2 3  
  
# 尝试第二次迭代(将会成功,因为每次迭代都创建了一个新的迭代器)  
for item in my_collection:  
    print(item)  # 输出: 1 2 3
二、python生成器是什么
  • 生成器的作用是,在需要用到一个东西的时候,生成需要的东西,而不是一下子都生成,从而会导致浪费资源。
  • 生成器是一种特殊的迭代器,即也拥有 next和 iter 方法。
  • python有两种方式提供生成器
    • 生成器函数
    • 生成器表达式
1、python有两种方式提供生成器------生成器函数
  • 如果一个函数内部有yield这个关键字,那么该函数就是一个生成器函数,调用生成器函数的时候,虽然看上去是调用函数,实际上是创建一个 生成器对象 。这个yield的作用和就是return 的返回的作用,但是yield不会跳出函数外。只是会暂停再这个位置。

  • 如果是第一次运行,即首次调用next()

    • 如果是第一次运行,就从def 的位置开始执行,一直运行到 yield 停下来。
    • 把yield 后面的关键字 返回。当作next()的返回值。
    • 生成器暂停本次运行,直到再次被next调用。
  • 如果不是第一次运行,即后续调用next()

    • 如果不是第一次运行,就从上一次的yield 暂停的语句的下一句开始执行,即上一次的yield 的下一句开始执行。
    • 调用next(),如果函数中存在其他yield语句,执行会再次在yield处暂停,并返回其后面的值。
    • 继续调用next(),如果函数中没有更多的yield语句,或者已经执行到了函数的末尾,那么生成器会引发StopIteration异常,表示没有更多的值可以产生了。
  • 通过这种方式,生成器允许我们逐步地、按需地从一个潜在的无限序列中获取值,而不需要一次性计算或存储整个序列。这使得生成器在处理大量数据或创建复杂迭代逻辑时非常高效。

def fib_generator():
    print("---1---")
    num1 = 1
    num2 = 1
    while True:
        print("---2---")
        temp_num = num1
        print("---3---")
        num1, num2 = num2, num1 + num2
        print("---4---")
        yield temp_num
        print("---5---")


fib = fib_generator()

print("执行第1次的next函数---开始--")
num = next(fib)
print("执行第1次的next函数---结束--")

print("打印第1次next返回的结果--开始--")
print(num)
print("打印第1次next返回的结果--结束--")

print("执行第2次的next函数---开始--")
num = next(fib)
print("执行第2次的next函数---结束--")

print("打印第2次next返回的结果--开始--")
print(num)
print("打印第2次next返回的结果--结束--")

2、python有两种方式提供生成器------生成器表达式

生成器表达式很简单,只要把一个列表生成式的 [ ] 改成 ( )

nums = [x for x in range(5)]
print(type(nums))
print(nums)

nums2 = (x for x in range(5))
print(type(nums2))
print(nums2)

在这里插入图片描述

三、python生成器的优点体现简单的例子

既然说生成器是一种特殊的迭代器,先用迭代器实现一个功能,再用生成器实现一下,简单对比一下。

class FibIterator(object):
    """斐波那契数列迭代器"""
    def __init__(self):
        # num1用来保存前前一个数,初始值为数列中的第一个数1
        self.num1 = 1
        # num2用来保存前一个数,初始值为数列中的第二个数1
        self.num2 = 1

    def __next__(self):
        """被next()函数调用来获取下一个数"""
        temp_num = self.num1
        self.num1, self.num2 = self.num2, self.num1+self.num2
        return temp_num

    def __iter__(self):
        """迭代器的__iter__返回自身即可"""
        return self


fib = FibIterator()

# 因为fib是迭代器所以不必使用iter()函数,直接使用next()函数即可
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))

可以看到上面的代码,虽然能够实现功能,但是稍稍有些复杂,下面是使用的生成器,

def fib_generator():   
    num1 = 1   
    num2 = 1   
    while True:   
        temp_num = num1   
        num1, num2 = num2, num1+num2   
        yield temp_num   

# 生成斐波那契数列   
fib = fib_generator()   
print(next(fib))   
print(next(fib))   
print(next(fib))   
print(next(fib))   

print(dir(fib)) #查看这个fib的方法,拥有iter和next就是迭代器  

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值