python的迭代器和生成器

迭代器和生成器

官方文档:
https://docs.python.org/zh-cn/3/tutorial/classes.html#iterators
https://docs.python.org/zh-cn/3/reference/expressions.html#generator-iterator-methods

可迭代对象

定义:实现了迭代协议的对象,能够使用for循环进行遍历的都叫可迭代对象。
Python中以下数据类型可以进行迭代:

  • 字符串
  • 列表
  • 元组
  • 集合
  • 字典

可以通过以下方法检测是否可迭代:

from collections import Iterable

# 字符串
s = "test"
print(isinstance(s, Iterable))  # 输出True

# 列表
li = [1, 2, 3, 4]
print(isinstance(li, Iterable))  # 输出True

# 元组
tup = (1, 2, 3, 4)
print(isinstance(tup, Iterable))  # 输出True

# 集合
se = {1, 2, 3, 4}
print(isinstance(se, Iterable))  # 输出True

# 字典
dic = {"name": "test"}
print(isinstance(dic, Iterable))  # 输出True

迭代协议

定义:可迭代对象实现了__iter__方法,那么这个对象就实现了迭代协议。

# 我们可以发现这几种数据类型中都存在__iter__方法
if "__iter__" in dir("12334"):
    print("True")

if "__iter__" in dir([1, 2]):
    print("True")

if "__iter__" in dir((1, 2)):
    print("True")

if "__iter__" in dir({1, 2}):
    print("True")

if "__iter__" in dir({1:2}):
    print("True")

# 打印一下这个方法,得到迭代器
print([1, 2].__iter__())
# 输出:<list_iterator object at 0x000001E4F58387F0>

# 查看列表迭代器相对于列表,多了哪些方法
print(set(dir([1, 2].__iter__())) - set(dir([1, 2])))
# 输出:{'__length_hint__', '__next__', '__setstate__'}

迭代器

定义

迭代器协议必须拥有__iter__和__next__方法。
实现了迭代器协议的对象叫迭代器。
注意点:所有的迭代器都是可迭代对象。

创建迭代器

  • iter(可迭代对象)
li = ["id", "title", "url", "data", "expected"]

# 将列表转换成迭代器
itor = iter(li)
print(itor)

# 打印结果: <list_iterator object at 0x000002882C40C0B8>

# 通过next方法对迭代器进行迭代操作
print(next(itor))  # 输出:id
print(next(itor))  # 输出:title
print(next(itor))  # 输出:url
print(next(itor))  # 输出:data
print(next(itor))  # 输出:expected
print(next(itor))  # 报错:StopIteration
  • 可迭代对象.__iter__()
# 创建一个迭代器
iter_1 = [1, 2, 3, 4, 5].__iter__()
# 获取迭代器中元素长度
print(iter_1.__length_hint__()) # 输出:5
# 根据索引值指定从哪里开始迭代
print(iter_1.__setstate__(2)) # 输出:None
# 一个个获取迭代内所有值
print(iter_1.__next__()) # 输出:3

这里需要特别注意, range是可迭代的,但是不是一个迭代器。

from collections import Iterable, Iterator

print(isinstance(range(5), Iterable))  # 输出:True
print(isinstance(range(5), Iterator))  # 输出:False

迭代器的特性

  • 迭代器能够使用next方法进行迭代操作。
  • 当迭代器中所有数据被迭代完之后,再使用next去迭代会抛出异常StopIteration。
  • 迭代器是不存储数据的,如果数据被遍历了,下次就无法遍历了。

迭代器的作用

  • 节约内存,提升程序的性能

生成器

生成器是一种特殊的迭代器,具备迭代器所有的特性。生成器内部不存储数据,只保存生成数据的计算规则。

生成器的应用:

  • 生成器表达式
  • 生成器函数

注意:数据比较简单规律就用生成器表达式。 数据比较复杂就用生成器函数。

生成器表达式

生成器表达式其实就是推导式的另一种应用。可以理解为元组推导式,但是返回的不是一个元组,而是一个生成器对象。

# 生成器表达式
a = (f"第{i}个数" for i in range(3))
# 以下两种方式使用next方法都是一样的
print(a.__next__()) # 输出:第0个数
print(next(a)) # 输出:第1个数

生成器函数

定义:只要函数中定义了yield这个关键字, 那么这就是一个生成器函数。生成器函数在调用的时候是不会直接执行的,会返回一个生成器对象。

生成器函数的执行:

  • 当我们使用next去获取生成器中的数据(使用next对生成器进行迭代操作),此时生成器函数会执行到yield处暂停,并且返回yield后面的数据。
  • 当生成器中数据都获取完之后,继续使用next方法会报错StopIteration。
# 定义一个生成器函数
def produce():
    for i in range(100):
        yield "第{}个数".format(i)

# 创建一个生成器对象
g = produce()
print(g)  # 输出:<generator object produce at 0x000001BDA33EDF68>
# 获取生成器中的数据
print(g.__next__())  # 输出:第0个数
print(g.__next__())  # 输出:第1个数
print(g.__next__())  # 输出:第2个数

生成器的作用

  • 延迟计算,有助于大数据处理
  • 提高代码可读性
  • 节约程序内存,提高性能

生成器和迭代器的区别

  • 生成器比迭代器多了三个方法:
    • send方法
    • close方法
    • throw方法
  • 生成器函数每次调用都会返回一个新的生成器,内存地址是不一样的。

send方法

  • send方法在生成数据的同时,可以给生成器内部传参。
  • send方法具有next方法功能的同时,还能进行传参操作。
  • send相当于进阶版的next。
  • send方法必须使用next生成过一次数据后才能调用,因为send方法只能从yield处开始执行。或者我们也可以使用send(None)启动生成器,再使用send方法发送实际值
  • 如果直接使用send方法,会报错:TypeError: can’t send non-None value to just-started generetor。
def func():
    for i in range(100):
        s = yield i
        print("send传进来的参数:", s)

# 创建生成器对象
f = func()

# 如果我们直接send会报错:TypeError: can't send non-None value to a just-started generator
# f.send(10)

# 使用send方法之前,必须先使用next方法生成一次数据
print(next(f))  # 输出0
print(f.send(1000))  # 输出send传进来的参数: 1000, 1

# 如果不传值也不会报错,只是会返回None
print(next(f))  # 输出send传进来的参数: None, 2

# ---------------或者这样启动生成器再send------------------------
f.send(None)
f.send(109)  # 输出: send传进来的参数: 109

send方法的场景应用:

def fun():
    num = 1
    for i in range(100):
        print(f"当前num的值{num}, i的值:{i}")
        s = yield num * i
        print(f"s的值:{s}")
        # 如果s有值,返回s, 否则返回num
        num = s or num

f = fun()
res = f.__next__()
print(res)
res = f.send(54)
print(res)
res = f.__next__()
print(res)
res = f.__next__()
print(res)
res = f.send(12)
print(res)
res = f.send("14")
print(res)

输出结果:
在这里插入图片描述

close方法

  • 生成器可以调用close方法进行关闭
  • 生成器关闭后就无法获取数据了。
  • 生成器关闭后不能重启,要使用必须新开一个。
def func():
    for i in range(100):
        s = yield i
        print("send传进来的参数:", s)

# 创建生成器对象
f = func()

print(next(f))  # 输出:0

# 关闭生成器
f.close()

print(next(f))  # 报错:StopIteration

throw方法

throw方法可以在生成器内部上一次暂停的yield处引发一个指定的异常类型,在生成器内部主动抛出异常。
def func():
    for i in range(100):
        try:
            yield i
        except TypeError:
            yield "TypeError异常返回的数据"
        except TimeoutError:
            yield "TimeoutError异常返回的数据"


# 创建生成器对象
f = func()
print(next(f))  # 输出:0

# 在生成器内部主动抛出异常
# print(f.throw(TypeError))  # 输出:TypeError异常返回的数据
print(f.throw(TimeoutError))  # 输出:TimeoutError异常返回的数据
# 注意:这里不能同时throw两个异常,会报错。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值