迭代器和生成器的学习笔记

news/2024/9/21 22:31:40 标签: 学习, 笔记, python

迭代器       

Python 迭代器是一种对象,它实现了迭代协议,包括 __iter__() 和 __next__() 方法。迭代器可以让你在数据集中逐个访问元素,而无需关心数据结构的底层实现。与列表或其他集合相比,迭代器可以节省内存,因为它们一次只生成一个元素。

迭代器的基本特点

  1. 懒加载:迭代器不会一次性将所有数据加载到内存中,而是根据需要生成元素。
  2. 状态保持:迭代器在迭代过程中会保持其状态,可以从上次停止的地方继续迭代。
  3. 可以遍历:迭代器是可遍历的,可以使用 for 循环等结构来进行遍历。

 下面的代码可以看出迭代器在节省内存方面的作用。

python">import sys
import random

# 生成 1-100的随机数的集合,共1000个元素
numbers = [random.randint(1, 100) for _ in range(1000)]
iterator = iter(numbers)

# 打印对象的内存大小
print(sys.getsizeof(numbers))   # 9016
print(sys.getsizeof(iterator))  # 48

 迭代器的经典demo:

python"># 创建一个简单的迭代器
class MyIterator:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.limit:
            self.current += 1
            return self.current
        else:
            raise StopIteration

# 使用迭代器
for num in MyIterator(5):
    print(num)

 迭代器在读取大文件的经典应用:

python">with open('users.csv', 'r') as file:
    for line in file:
        print(line.strip())

         逐行读取文件内容,而不需要将整个文件加载到内存中。这种方法节省了内存资源,特别是在处理非常大的文件时。

生成器

生成器是一种特殊类型的迭代器,它允许你在函数中暂停执行并在以后继续。 

使用 yield 的基本概念

  1. 生成器函数:当一个函数包含yield语句时,它不再是一个普通的函数,而是一个生成器函数。当调用这个函数时,它不会立即执行,而是返回一个生成器对象。

  2. 状态保留:每次调用生成器的__next__()方法(或者使用next()函数)时,生成器函数会从上次执行的位置继续执行,直到遇到下一个yield语句。在此时,函数的执行状态(包括局部变量)会被保留。

  3. 迭代:生成器可以被用于迭代,像普通的列表或其他可迭代对象一样。使用for循环可以逐个获取生成器产生的值。

python"># 定义一个生成器函数
def read_users():
    with open('users.csv', 'r') as file:
        for line in file:
            yield line.strip()


r1 = read_users()  # 定义一个生成器对象
r2 = read_users()  # 定义另一个生成器对象
print(next(r1))
print(next(r1))
print(next(r1))
print(next(r1))
print(next(r2))
print(next(r2))
print(next(r2))
print(next(r2))

局部变量保留的demo

python">list1 = ['a', 'b', 'c', 'd', 'e', 'f']


def iterator():
    i = 0
    for x in list1:
        yield do_something(i, x)
        i += 1


def do_something(i, x):
    print(i, x)


r1 = iterator()  # 定义一个生成器对象
r2 = iterator()  # 定义另一个生成器对象
next(r1)
next(r1)
next(r1)
next(r1)
next(r2)
next(r2)
next(r2)
next(r2)
next(r2)
运行结果:
0 a
1 b
2 c
3 d
0 a
1 b
2 c
3 d
4 e

更深入理解yield的“暂停”

函数每次遇到yield就暂停,它并不在意yield时来自于循环、迭代或者是在函数体内重复定义的,这意味着一个函数中可以有不止一个yield,并且每次运行到yield时,函数就暂停运行,并保存中间结果和变量,直到下一次next()后继续运行。

python">list1 = ['a', 'b', 'c', 'd', 'e', 'f']


def iterator():
    for x in list1:
        yield print(x)
        yield print(x)
        yield print(x)
        yield print(x)


r1 = iterator()  # 定义一个生成器对象
next(r1)
next(r1)
next(r1)
next(r1)
next(r1)
next(r1)

# 运行结果:
# a
# a
# a
# a
# b
# b

可以将yield理解为一个中断标志

        可以将yield理解为一个中断标志,当生成器遇到 yield 语句时,它会暂停执行,并返回 yield 后面跟随的值或者函数。如果yield后面没有跟随内容,那么它就仅仅是一次暂停标志而已。

python">list1 = ['a', 'b', 'c', 'd', 'e', 'f']


def iterator():
    i = 0
    for x in list1:
        print('loop', i)
        yield
        i += 1
        print(x)


r1 = iterator()  # 定义一个生成器对象
next(r1)
next(r1)
next(r1)
# 执行结果:
# loop 0
# a
# loop 1
# b
# loop 2

 yield的灵活运用

既然函数每次遇到yield就暂停,它并不在意yield时来自于循环、迭代或者是在函数体内重复定义的,而且可以将yield理解为一个中断标志,那么我们也就可以生成一个不循环的函数,通过yield达到步进执行的效果。

python">list1 = ['a', 'b', 'c', 'd', 'e', 'f']


def iterator():
    i = 0
    print('breakpoint', i)
    yield
    i += 1
    print('breakpoint', i)
    yield
    i += 1
    print('breakpoint', i)
    yield
    i += 1
    print('breakpoint', i)
    yield
    i += 1
    print('breakpoint', i)


r1 = iterator()  # 定义一个生成器对象
next(r1)
next(r1)
next(r1)
# 执行结果:
# breakpoint 0
# breakpoint 1
# breakpoint 2

注意可暂停和可迭代次数

要保证调用的次数不要大于可迭代次数或者可暂停次数,否则就会报错。

python">def iterator():
    i = 0
    print('breakpoint', i)
    i += 1
    yield
    print('breakpoint', i)
    i += 1
    yield


r1 = iterator()  # 定义一个生成器对象
next(r1)
next(r1)
next(r1)

上面的例子,定义了两个yield,但是next()调用了三次,所以出错。 

python">list1 = [1, 2, 3]


def iterator():
    for i in list1:  # 遍历列表
        yield print(i)


r1 = iterator()  # 定义一个生成器对象
next(r1)
next(r1)
next(r1)
next(r1)

这个,由于调用次数大于了列表的元素数量,也会出错。

采取措施,避免程序崩溃

1、使用try

python">def iterator():
    i = 0
    print('breakpoint', i)
    i += 1
    yield
    print('breakpoint', i)
    i += 1
    yield


r1 = iterator()  # 定义一个生成器对象


def next_do():
    try:
        next(r1)
    except StopIteration:
        print("No more items to yield")


next_do()
next_do()
next_do()
next_do()
python">list1 = [1, 2, 3]


def iterator():
    for i in list1:  # 遍历列表
        yield print(i)


r1 = iterator()  # 定义一个生成器对象


def next_do():
    try:
        next(r1)
    except StopIteration:
        print("No more items to yield")


next_do()
next_do()
next_do()
next_do()
next_do()

# 运行结果:
# 1
# 2
# 3
# No more items to yield
# No more items to yield

2、指定next()的默认返回值参数,如果指定了该参数,并且迭代器没有更多的值可返回,则返回该参数的值,而不是引发 StopIteration 异常。

python">def iterator():
    i = 0
    print('breakpoint', i)
    i += 1
    yield
    print('breakpoint', i)
    i += 1
    yield


def next_do():
    if next(r1, 'END'):  # 指定next()的默认返回值,可以是任意非None值
        print('No more items to yield')


r1 = iterator()  # 定义一个生成器对象

next_do()
next_do()
next_do()
next_do()
next_do()

# 运行结果:
# breakpoint 0
# breakpoint 1
# No more items to yield
# No more items to yield
# No more items to yield

send()的用法

python">def generator_with_send():
    received = yield  # yield 语句会暂停生成器,等待 send() 方法的调用并返回 yield 语句后面的值
    yield received  # 输出接收到的值


gen = generator_with_send()

next(gen)  # 启动生成器
print(gen.send('this is send1'))  # 通过 send() 方法向生成器发送数据

 多次发送

python">def generator_with_send():
    received = yield  # 第一次调用 `send` 时暂停在此处
    yield received  # 输出第一次接收到的值
    received = yield  # 再次暂停,准备接收下一个值
    yield received  # 输出第二次接收到的值
    received = yield  # 再次暂停,准备接收下一个值
    yield received  # 输出第三次接收到的值


gen = generator_with_send()

next(gen)  # 启动生成器
print(gen.send('this is send1'))  # 发送数据
next(gen)  # 继续执行,准备接收下一个值
print(gen.send('this is send2'))  # 发送数据
next(gen)  # 继续执行,准备接收下一个值
print(gen.send('this is send3'))  # 发送数据


# 输出结果为:
# this is send1
# this is send2
# this is send3

OR:

python">def generator_with_send():
    received = yield  # 第一次调用 `send` 时暂停在此处

    received = yield received  # 每次重新接收 send() 发送的值
    received = yield received  # 每次重新接收 send() 发送的值
    received = yield received  # 每次重新接收 send() 发送的值


gen = generator_with_send()

next(gen)  # 启动生成器
print(gen.send('this is send1'))  # 第一次发送 'this is send1'
print(gen.send('this is send2'))  # 第二次发送 'this is send2'
print(gen.send('this is send3'))  # 第三次发送 'this is send3'

 

 OR:

python">def generator_with_send():
    received = yield  # 第一次调用 `send` 时暂停在此处
    while True:
        received = yield received  # 每次重新接收 send() 发送的值


gen = generator_with_send()

next(gen)  # 启动生成器
print(gen.send('this is send1'))  # 第一次发送 'this is send1'
print(gen.send('this is send2'))  # 第二次发送 'this is send2'
print(gen.send('this is send3'))  # 第三次发送 'this is send3'

close()的用法

        close()方法用于关闭生成器,关闭后,如果尝试迭代或恢复执行生成器,会引发 StopIteration 异常。
        在生成器中,close() 方法会触发 GeneratorExit 异常,通过捕捉这个异常,在生成器中进行必要的清理工作(比如释放资源、关闭文件等)。

python">def my_generator():
    try:
        while True:
            value = yield
            print(f"Received: {value}")
    except GeneratorExit:
        print("Generator is being closed.")

gen = my_generator()

# 启动生成器
next(gen)

# 发送一些值
gen.send('Hello')
gen.send('World')

# 关闭生成器
gen.close()

# 再次尝试发送值会引发 StopIteration 异常
try:
    gen.send('This will not work')
except StopIteration:
    print("Generator is closed and cannot receive values.")

 


http://www.niftyadmin.cn/n/5669559.html

相关文章

地平线4登录xbox后提示需要登录档案怎么解决

这个游戏是真nt&#xff08;在联机上&#xff09;&#xff0c;典型搞联机2小时游玩半小时&#xff0c;多半时间都花费在联机上了&#xff0c;不是为了联机和朋友跑车&#xff0c;早给他卸载了。 本人的游戏问题&#xff1a;看了一些视频感觉没什么作用&#xff0c;我的现象就是…

linux文件同步、传输

使用rsync 适合用于大量文件多次同步&#xff0c;此工具是增量的同步&#xff0c;即如果之前同步过一次了&#xff0c;后续再同步&#xff0c;他只会传输有变化、新增的文件&#xff0c;就是之前同步过的文件如果没有变化&#xff08;文件大小、修改时间等判断&#xff09;就不…

Gradio离线部署到内网,资源加载失败问题(Gradio离线部署问题解决方法)

问题描述 Gradio作为一个快速构建一个演示或Web应用的开源Python包&#xff0c;被广泛使用&#xff0c;最近在用这个包进行AI应用构建&#xff0c;打包部署到内网Docker的时候发现有些资源无法使用。网页加载不出来。即使加载出来了也是没有样式无法点击的。 一般出现这个问题…

谷歌 Chrome 最新版升级:更强的安全检查功能守护你的上网安全

谷歌 Chrome 浏览器产品经理 Andrew Kamau 在最新发布的博文中宣布&#xff0c;Chrome 浏览器迎来了新一轮的安全升级。新版 Chrome 在后台自动运行安全检查功能&#xff0c;采取了额外的主动措施来保障用户的安全。 自动撤销通知权限 新版 Chrome 浏览器采用了一项基于谷歌安…

markdown-it:将Markdown文本转换为HTML格式,展示在页面,怎么自定义里面的a标签设置为在新标签页打开

由markdown-it将文本生成html然后渲染到页面上&#xff0c;但是现在你点击里面生成好的链接只能在本标签页打开&#xff0c;怎么将其设置为在新标签打开呢&#xff1f; 安装markdown-it npm install markdown-it 使用markdown-it const mdi new MarkdownIt({html: true,l…

在 CentOS 中安装 MySQL(无坑版)

1. 下载安装 MySQL yum 仓库 请按照自己的系统版本选择自己喜欢的 MySQL版本 uname -a 或者 lsb_release -aMySQL yum 仓库地址&#xff1a; https://repo.mysql.com/ CentOS 8 wget https://repo.mysql.com/mysql80-community-release-el8-1.noarch.rpm yum localinstall…

【Qt | QLineEdit】Qt 中使 QLineEdit 响应 鼠标单击、双击事件 的两个方法

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a; 2024-09-14 …

汽车美容服务管理系统的数据库设计与数据操作

设计内容&#xff1a; 考察汽车美容服务公司的实际业务运作&#xff0c;设计汽车美容服务综合业务管理系统的数据库。 1.系统主要功能 &#xff08;1&#xff09;基础数据管理。包括汽车美容服务项目&#xff08;按不同类别&#xff09; 管理&#xff08;增加、更新、删除、多条…