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

十念  金牌会员 | 2024-9-22 02:39:28 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 829|帖子 829|积分 2487

迭代器       

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


  • 懒加载:迭代器不会一次性将所有数据加载到内存中,而是根据需要生成元素。
  • 状态保持:迭代器在迭代过程中会保持其状态,可以从上次停止的地方继承迭代。
  • 可以遍历:迭代器是可遍历的,可以使用 for 循环等布局来举行遍历。
 下面的代码可以看出迭代器在节流内存方面的作用。
  1. import sys
  2. import random
  3. # 生成 1-100的随机数的集合,共1000个元素
  4. numbers = [random.randint(1, 100) for _ in range(1000)]
  5. iterator = iter(numbers)
  6. # 打印对象的内存大小
  7. print(sys.getsizeof(numbers))   # 9016
  8. print(sys.getsizeof(iterator))  # 48
复制代码
 迭代器的经典demo:
  1. # 创建一个简单的迭代器
  2. class MyIterator:
  3.     def __init__(self, limit):
  4.         self.limit = limit
  5.         self.current = 0
  6.     def __iter__(self):
  7.         return self
  8.     def __next__(self):
  9.         if self.current < self.limit:
  10.             self.current += 1
  11.             return self.current
  12.         else:
  13.             raise StopIteration
  14. # 使用迭代器
  15. for num in MyIterator(5):
  16.     print(num)
复制代码
 迭代器在读取大文件的经典应用:
  1. with open('users.csv', 'r') as file:
  2.     for line in file:
  3.         print(line.strip())
复制代码
         逐行读取文件内容,而不需要将整个文件加载到内存中。这种方法节流了内存资源,特别是在处置惩罚非常大的文件时。
生成器

生成器是一种特别类型的迭代器,它允许你在函数中暂停执行并在以后继承。 
使用 yield 的基本概念


  • 生成器函数:当一个函数包含yield语句时,它不再是一个普通的函数,而是一个生成器函数。当调用这个函数时,它不会立即执行,而是返回一个生成器对象。
  • 状态保存:每次调用生成器的__next__()方法(大概使用next()函数)时,生成器函数会从上次执行的位置继承执行,直到碰到下一个yield语句。在此时,函数的执行状态(包括局部变量)会被保存。
  • 迭代:生成器可以被用于迭代,像普通的列表或其他可迭代对象一样。使用for循环可以逐个获取生成器产生的值。
  1. # 定义一个生成器函数
  2. def read_users():
  3.     with open('users.csv', 'r') as file:
  4.         for line in file:
  5.             yield line.strip()
  6. r1 = read_users()  # 定义一个生成器对象
  7. r2 = read_users()  # 定义另一个生成器对象
  8. print(next(r1))
  9. print(next(r1))
  10. print(next(r1))
  11. print(next(r1))
  12. print(next(r2))
  13. print(next(r2))
  14. print(next(r2))
  15. print(next(r2))
复制代码
局部变量保存的demo

  1. list1 = ['a', 'b', 'c', 'd', 'e', 'f']
  2. def iterator():
  3.     i = 0
  4.     for x in list1:
  5.         yield do_something(i, x)
  6.         i += 1
  7. def do_something(i, x):
  8.     print(i, x)
  9. r1 = iterator()  # 定义一个生成器对象
  10. r2 = iterator()  # 定义另一个生成器对象
  11. next(r1)
  12. next(r1)
  13. next(r1)
  14. next(r1)
  15. next(r2)
  16. next(r2)
  17. next(r2)
  18. next(r2)
  19. next(r2)
  20. 运行结果:
  21. 0 a
  22. 1 b
  23. 2 c
  24. 3 d
  25. 0 a
  26. 1 b
  27. 2 c
  28. 3 d
  29. 4 e
复制代码
更深入理解yield的“暂停”

函数每次碰到yield就暂停,它并不在意yield时来自于循环、迭代大概是在函数体内重复定义的,这意味着一个函数中可以有不止一个yield,并且每次运行到yield时,函数就暂停运行,并保存中间效果和变量,直到下一次next()后继承运行。
  1. list1 = ['a', 'b', 'c', 'd', 'e', 'f']
  2. def iterator():
  3.     for x in list1:
  4.         yield print(x)
  5.         yield print(x)
  6.         yield print(x)
  7.         yield print(x)
  8. r1 = iterator()  # 定义一个生成器对象
  9. next(r1)
  10. next(r1)
  11. next(r1)
  12. next(r1)
  13. next(r1)
  14. next(r1)
  15. # 运行结果:
  16. # a
  17. # a
  18. # a
  19. # a
  20. # b
  21. # b
复制代码
可以将yield理解为一个中断标记

        可以将yield理解为一个中断标记,当生成器碰到 yield 语句时,它会暂停执行,并返回 yield 背面跟随的值大概函数。假如yield背面没有跟随内容,那么它就仅仅是一次暂停标记而已。
  1. list1 = ['a', 'b', 'c', 'd', 'e', 'f']
  2. def iterator():
  3.     i = 0
  4.     for x in list1:
  5.         print('loop', i)
  6.         yield
  7.         i += 1
  8.         print(x)
  9. r1 = iterator()  # 定义一个生成器对象
  10. next(r1)
  11. next(r1)
  12. next(r1)
  13. # 执行结果:
  14. # loop 0
  15. # a
  16. # loop 1
  17. # b
  18. # loop 2
复制代码
 yield的灵活运用

既然函数每次碰到yield就暂停,它并不在意yield时来自于循环、迭代大概是在函数体内重复定义的,而且可以将yield理解为一个中断标记,那么我们也就可以生成一个不循环的函数,通过yield达到步进执行的效果。
  1. list1 = ['a', 'b', 'c', 'd', 'e', 'f']
  2. def iterator():
  3.     i = 0
  4.     print('breakpoint', i)
  5.     yield
  6.     i += 1
  7.     print('breakpoint', i)
  8.     yield
  9.     i += 1
  10.     print('breakpoint', i)
  11.     yield
  12.     i += 1
  13.     print('breakpoint', i)
  14.     yield
  15.     i += 1
  16.     print('breakpoint', i)
  17. r1 = iterator()  # 定义一个生成器对象
  18. next(r1)
  19. next(r1)
  20. next(r1)
  21. # 执行结果:
  22. # breakpoint 0
  23. # breakpoint 1
  24. # breakpoint 2
复制代码
留意可暂停和可迭代次数

要包管调用的次数不要大于可迭代次数大概可暂停次数,否则就会报错。
  1. def iterator():
  2.     i = 0
  3.     print('breakpoint', i)
  4.     i += 1
  5.     yield
  6.     print('breakpoint', i)
  7.     i += 1
  8.     yield
  9. r1 = iterator()  # 定义一个生成器对象
  10. next(r1)
  11. next(r1)
  12. next(r1)
复制代码
上面的例子,定义了两个yield,但是next()调用了三次,所以出错。 
  1. list1 = [1, 2, 3]
  2. def iterator():
  3.     for i in list1:  # 遍历列表
  4.         yield print(i)
  5. r1 = iterator()  # 定义一个生成器对象
  6. next(r1)
  7. next(r1)
  8. next(r1)
  9. next(r1)
复制代码
这个,由于调用次数大于了列表的元素数量,也会出错。
采取措施,避免步伐崩溃

1、使用try
  1. def iterator():
  2.     i = 0
  3.     print('breakpoint', i)
  4.     i += 1
  5.     yield
  6.     print('breakpoint', i)
  7.     i += 1
  8.     yield
  9. r1 = iterator()  # 定义一个生成器对象
  10. def next_do():
  11.     try:
  12.         next(r1)
  13.     except StopIteration:
  14.         print("No more items to yield")
  15. next_do()
  16. next_do()
  17. next_do()
  18. next_do()
复制代码
  1. list1 = [1, 2, 3]
  2. def iterator():
  3.     for i in list1:  # 遍历列表
  4.         yield print(i)
  5. r1 = iterator()  # 定义一个生成器对象
  6. def next_do():
  7.     try:
  8.         next(r1)
  9.     except StopIteration:
  10.         print("No more items to yield")
  11. next_do()
  12. next_do()
  13. next_do()
  14. next_do()
  15. next_do()
  16. # 运行结果:
  17. # 1
  18. # 2
  19. # 3
  20. # No more items to yield
  21. # No more items to yield
复制代码
2、指定next()的默认返回值参数,假如指定了该参数,并且迭代器没有更多的值可返回,则返回该参数的值,而不是引发 StopIteration 非常。
  1. def iterator():
  2.     i = 0
  3.     print('breakpoint', i)
  4.     i += 1
  5.     yield
  6.     print('breakpoint', i)
  7.     i += 1
  8.     yield
  9. def next_do():
  10.     if next(r1, 'END'):  # 指定next()的默认返回值,可以是任意非None值
  11.         print('No more items to yield')
  12. r1 = iterator()  # 定义一个生成器对象
  13. next_do()
  14. next_do()
  15. next_do()
  16. next_do()
  17. next_do()
  18. # 运行结果:
  19. # breakpoint 0
  20. # breakpoint 1
  21. # No more items to yield
  22. # No more items to yield
  23. # No more items to yield
复制代码
send()的用法

  1. def generator_with_send():
  2.     received = yield  # yield 语句会暂停生成器,等待 send() 方法的调用并返回 yield 语句后面的值
  3.     yield received  # 输出接收到的值
  4. gen = generator_with_send()
  5. next(gen)  # 启动生成器
  6. print(gen.send('this is send1'))  # 通过 send() 方法向生成器发送数据
复制代码
 多次发送
  1. def generator_with_send():
  2.     received = yield  # 第一次调用 `send` 时暂停在此处
  3.     yield received  # 输出第一次接收到的值
  4.     received = yield  # 再次暂停,准备接收下一个值
  5.     yield received  # 输出第二次接收到的值
  6.     received = yield  # 再次暂停,准备接收下一个值
  7.     yield received  # 输出第三次接收到的值
  8. gen = generator_with_send()
  9. next(gen)  # 启动生成器
  10. print(gen.send('this is send1'))  # 发送数据
  11. next(gen)  # 继续执行,准备接收下一个值
  12. print(gen.send('this is send2'))  # 发送数据
  13. next(gen)  # 继续执行,准备接收下一个值
  14. print(gen.send('this is send3'))  # 发送数据
  15. # 输出结果为:
  16. # this is send1
  17. # this is send2
  18. # this is send3
复制代码
OR:
  1. def generator_with_send():
  2.     received = yield  # 第一次调用 `send` 时暂停在此处
  3.     received = yield received  # 每次重新接收 send() 发送的值
  4.     received = yield received  # 每次重新接收 send() 发送的值
  5.     received = yield received  # 每次重新接收 send() 发送的值
  6. gen = generator_with_send()
  7. next(gen)  # 启动生成器
  8. print(gen.send('this is send1'))  # 第一次发送 'this is send1'
  9. print(gen.send('this is send2'))  # 第二次发送 'this is send2'
  10. print(gen.send('this is send3'))  # 第三次发送 'this is send3'
复制代码
 
 OR:
  1. def generator_with_send():
  2.     received = yield  # 第一次调用 `send` 时暂停在此处
  3.     while True:
  4.         received = yield received  # 每次重新接收 send() 发送的值
  5. gen = generator_with_send()
  6. next(gen)  # 启动生成器
  7. print(gen.send('this is send1'))  # 第一次发送 'this is send1'
  8. print(gen.send('this is send2'))  # 第二次发送 'this is send2'
  9. print(gen.send('this is send3'))  # 第三次发送 'this is send3'
复制代码
close()的用法

        close()方法用于关闭生成器,关闭后,假如实验迭代或规复执行生成器,会引发 StopIteration 非常。
        在生成器中,close() 方法会触发 GeneratorExit 非常,通过捕获这个非常,在生成器中举行必要的清理工作(比如释放资源、关闭文件等)。
  1. def my_generator():
  2.     try:
  3.         while True:
  4.             value = yield
  5.             print(f"Received: {value}")
  6.     except GeneratorExit:
  7.         print("Generator is being closed.")
  8. gen = my_generator()
  9. # 启动生成器
  10. next(gen)
  11. # 发送一些值
  12. gen.send('Hello')
  13. gen.send('World')
  14. # 关闭生成器
  15. gen.close()
  16. # 再次尝试发送值会引发 StopIteration 异常
  17. try:
  18.     gen.send('This will not work')
  19. except StopIteration:
  20.     print("Generator is closed and cannot receive values.")
复制代码
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表