迭代器
Python 迭代器是一种对象,它实现了迭代协议,包括 __iter__() 和 __next__() 方法。迭代器可以让你在数据集中逐个访问元素,而无需关心数据布局的底层实现。与列表或其他集合相比,迭代器可以节流内存,因为它们一次只生成一个元素。
迭代器的基本特点
- 懒加载:迭代器不会一次性将所有数据加载到内存中,而是根据需要生成元素。
- 状态保持:迭代器在迭代过程中会保持其状态,可以从上次停止的地方继承迭代。
- 可以遍历:迭代器是可遍历的,可以使用 for 循环等布局来举行遍历。
下面的代码可以看出迭代器在节流内存方面的作用。
- 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:
- # 创建一个简单的迭代器
- 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)
复制代码 迭代器在读取大文件的经典应用:
- with open('users.csv', 'r') as file:
- for line in file:
- print(line.strip())
复制代码 逐行读取文件内容,而不需要将整个文件加载到内存中。这种方法节流了内存资源,特别是在处置惩罚非常大的文件时。
生成器
生成器是一种特别类型的迭代器,它允许你在函数中暂停执行并在以后继承。
使用 yield 的基本概念
- 生成器函数:当一个函数包含yield语句时,它不再是一个普通的函数,而是一个生成器函数。当调用这个函数时,它不会立即执行,而是返回一个生成器对象。
- 状态保存:每次调用生成器的__next__()方法(大概使用next()函数)时,生成器函数会从上次执行的位置继承执行,直到碰到下一个yield语句。在此时,函数的执行状态(包括局部变量)会被保存。
- 迭代:生成器可以被用于迭代,像普通的列表或其他可迭代对象一样。使用for循环可以逐个获取生成器产生的值。
- # 定义一个生成器函数
- 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
- 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()后继承运行。
- 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背面没有跟随内容,那么它就仅仅是一次暂停标记而已。
- 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达到步进执行的效果。
- 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
复制代码 留意可暂停和可迭代次数
要包管调用的次数不要大于可迭代次数大概可暂停次数,否则就会报错。
- 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()调用了三次,所以出错。
- 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
- 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()
复制代码- 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 非常。
- 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()的用法
- def generator_with_send():
- received = yield # yield 语句会暂停生成器,等待 send() 方法的调用并返回 yield 语句后面的值
- yield received # 输出接收到的值
- gen = generator_with_send()
- next(gen) # 启动生成器
- print(gen.send('this is send1')) # 通过 send() 方法向生成器发送数据
复制代码 多次发送
- 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:
- 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:
- 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 非常,通过捕获这个非常,在生成器中举行必要的清理工作(比如释放资源、关闭文件等)。
- 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.")
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |