Python学习之迭代器和生成器

当说起Python里面的高级特性时,就不能不提迭代器(Iterators)和生成器(Generators)啦!它们就像是处理数据的一把利器,特别是对付大数据的时候,简直就是神器!咱们今天就来聊聊它们到底是啥,怎么用,还有点啥实际用途吧!

1. 迭代器(Iterators)

🌾 什么是迭代器

迭代Python 最强大的功能之一,而迭代器(Iterators)是一种用于遍历集合的对象。它能记住遍历的位置,并提供一个方法来访问集合中的下一个元素。有两个特点:

  • 只能向前遍历,不能后退。
  • 使用 for 循环或 next() 函数进行遍历。
🌾 迭代器的工作原理

迭代器的工作原理是实现迭代器协议。该协议定义了两个方法:

  • iter(): 返回一个新的迭代器对象。
  • next(): 返回迭代器中的下一个元素。

当迭代器到达集合的末尾时,next() 方法会抛出 StopIteration 异常。

以下是一些可迭代对象的示例:

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

2. 使用迭代器

下面通过两个例子讲一下迭代器。

🌾 示例1:使用 for 循环来遍历迭代器。
#🌾 定义列表变量
my_list = [1, 2, 3, 4, 5] #这里的集合是一个列表

#🌾 使用 for 循环遍历列表
for item in my_list:
    print(item)

#🌾 输出:
# 1
# 2
# 3
# 4
# 5

该代码使用 while True 循环来遍历迭代器。在循环体内,它使用 next() 方法获取迭代器的下一个元素。如果迭代器中没有更多元素,则 next() 方法会引发 StopIteration 异常。

最后,该代码使用 except 语句来处理 StopIteration 异常。当 StopIteration 异常被引发时,该代码会跳出循环。

🌾 示例2:使用 next() 方法手动遍历迭代器。
#🌾 定义列表变量
my_list = [1, 2, 3, 4, 5] #这里的集合是一个列表

#🌾 使用 iter() 函数将列表转换为迭代器。
my_iterator = iter(my_list)

#🌾 使用 next() 方法手动遍历列表
while True:
    try:
        item = next(my_iterator)
        print(item)
    except StopIteration:
        break

#🌾 输出:
# 1
# 2
# 3
# 4
# 5

3. 自定义迭代器

前面我们使用用 iter() 函数将列表转换为迭代器。当然,也可以创建自定义迭代器来遍历任何类型的数据集。例如,可以创建一个迭代器来遍历一个字符串:

#🌾 定义类
class MyStringIterator:
    def __init__(self, string):
        # 将字符串存储为属性
        self.string = string
        # 将索引初始化为 0
        self.index = 0

    def __iter__(self):
        # 返回自身以指示它是迭代器
        return self

    def __next__(self):
        # 检查是否已到达字符串末尾
        if self.index < len(self.string):
            # 获取当前索引处的字符
            item = self.string[self.index]
            # 将索引增加 1 以指向下一个字符
            self.index += 1
            # 返回字符
            return item
        else:
            # 引发 StopIteration 异常以指示迭代结束
            raise StopIteration

my_string = "Hello, world!"

#🌾 使用自定义迭代器遍历字符串
for item in MyStringIterator(my_string):
    print(item)

输出:

H
e
l
l
o
,
 
w
o
r
l
d
!
🔊:关于迭代器,需要记住所有迭代器都是可迭代对象,但并非所有可迭代对象都是迭代器。例如,字符串是可迭代对象,但不是迭代器。

4. 生成器(Generators)

🌾 生成器是什么?

生成器(Generators)是一种特殊的迭代器,通过yield 关键字来创建,它可以延迟执行,并逐个生成值。

🌾 使用生成器表达式:
#🌾 生成器表达式
my_generator = (x for x in range(10))

#🌾 遍历生成器
for item in my_generator:
    print(item)

#🌾 输出:
# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9

使用 yield 关键字创建生成器函数

#🌾 生成器函数
def my_generator():
    for x in range(10):
        yield x

#🌾 遍历生成器
for item in my_generator():
    print(item)
    
#🌾 输出:0 1 2 3 4 5 6 7 8 9 

5. 生成器 vs. 列表推导式

特性生成器列表推导式
返回数据类型 生成器 列表
内存占用 较少 较多
计算方式 惰性计算 立即计算
适用场景 数据量较大、需要多次迭代 数据量较小、不需要多次迭代
🌾 详细说明

 生成器

  • 生成器是一个返回迭代器的函数,只能用于迭代操作。
  • 生成器对象具有惰性求值的特点,即只有需要的时候才生成元素。
  • 可以通过内置函数 next() 或生成器对象的 __next__() 方法获取下一个值。

 列表推导式

  • 列表推导式是一种用来创建列表的语法糖。
  • 列表推导式会立即计算并生成整个列表。
  • 列表推导式的语法与生成器表达式相似,但返回的是列表而不是生成器。
🌾 选择建议
  • 如果要多次迭代,建议使用列表推导式。
  • 如果数组很大或者有无穷个元素,建议使用生成器表达式。

6. 生成器的惰性计算(Lazy Evaluation)

惰性计算的优点

  • 节省内存:只生成需要的值,不会占用额外的内存空间。
  • 提高效率:避免了生成不必要的值,提高了计算效率。
🌾 生成器表达式的惰性特性

生成器表达式具有惰性特性,即只在需要时才计算值。例如:

#🌾 生成 1 到 10 的平方数
my_generator = (x**2 for x in range(1, 11))

#🌾 仅获取前三个平方数
for item in my_generator:
    print(item)
    if item >= 9:
        break
🌾 逐步获取生成器的值

可以使用 next() 方法逐步获取生成器的值:

#🌾 定义迭代器
my_generator = (x**2 for x in range(1, 11))
#🌾 逐步获取生成器的值 while True: try: item = next(my_generator) print(item) except StopIteration: break # 输出: # 1 # 4 # 9 # 16 # 25 # 36 # 49 # 64 # 81 # 100

7. 生成器的状态管理

🌾 send() 方法:使用 send() 方法向生成器发送值。

#🌾 迭代器函数
def my_generator():
    x = 0
    while True:
        x = yield x
        print("Received:", x)

#🌾 使用 send() 方法向生成器发送值
my_generator = my_generator()
next(my_generator)  # 初始化生成器
my_generator.send(10) # Received: 10
my_generator.send(20) # Received: 20

🌾 throw() 方法:使用 throw() 方法向生成器抛出异常。

def my_generator():
    try:
        while True:
            yield
    except Exception as e:
        print("Error:", e) 

#🌾 使用 throw() 方法向生成器抛出异常
my_generator = my_generator()
next(my_generator)
my_generator.throw(ValueError("Error ")) #Error

🌾 close() 方法:使用 close() 方法关闭生成器。

def my_generator():
    try:
        while True:
            yield
    finally:
        print("Generator closed")

#🌾 使用 close() 方法关闭生成器
my_generator = my_generator()
next(my_generator)
my_generator.close()  # 输出:Generator closed

8. 生成器的并发与异步

生成器是一个可以暂停和恢复执行的函数,它会生成一个值并暂停,然后等待下一次调用时再继续执行。这使得它们非常适合处理大量的数据或在异步编程中执行并发操作。

🌾 使用生成器实现并发

生成器可以与线程一起使用,从而实现一些形式的并发操作。Python 的 concurrent.futures 模块提供了 ThreadPoolExecutor 和 ProcessPoolExecutor,这些可以与生成器一起使用,允许你并发地运行多个生成器函数。

import concurrent.futures

def my_generator_function(data):
    for item in data:
        # 执行一些操作
        yield processed_item

def main():
    data = [1, 2, 3, 4, 5]
    with concurrent.futures.ThreadPoolExecutor() as executor:
        results = list(executor.map(my_generator_function, data))
    print(results)

if __name__ == "__main__":
    main()

这里的 executor.map 函数允许你并发地调用 my_generator_function 函数,并且结果会被收集到一个列表中。

🌾 异步生成器和 async/await

异步生成器允许你在生成器函数中使用 await 关键字,这使得你可以等待其他异步操作完成,而不会阻塞整个生成器。

在 Python 3.6 及更高版本中,你可以使用 async def 定义异步生成器函数,使用 yield 语句生成值:

async def async_generator_function(data):
    for item in data:
        # 进行一些异步操作
        yield processed_item

然后你可以使用 async for 循环来遍历异步生成器产生的值:

async def main():
    async for item in async_generator_function(data):
        print(item)

if __name__ == "__main__":
    import asyncio
    data = [1, 2, 3, 4, 5]
    asyncio.run(main())

这里的 async for 循环允许你在异步函数中迭代异步生成器的值,而不会阻塞事件循环。

posted on 2024-12-04 19:03  梁飞宇  阅读(1292)  评论(0)    收藏  举报