"/>
侧边栏壁纸
博主头像
PySuper博主等级

千里之行,始于足下

  • 累计撰写 204 篇文章
  • 累计创建 14 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Python 迭代器 生成器

PySuper
2024-06-19 / 0 评论 / 0 点赞 / 67 阅读 / 13484 字
温馨提示:
所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

迭代器

迭代器时一个可以记住遍历位置的对象

迭代器对象 从第一个元素开始访问,直到所有元素被访问完结束

迭代器只能前进,不能后退

可迭代对象

实现了 __iter__() 方法的对象

或者实现了可以返回一个迭代器的 __getitem__() 方法(支持下标索引)

  • 每迭代一次,都会返回对象中的下一个元素

  • 一直向后读取,知道迭代了所有的数据

# 列表是一个可迭代对象
my_list = [1, 2, 3]

# 使用 for 循环遍历可迭代对象
for item in my_list:
    print(item)

# 使用 iter() 函数获取迭代器
my_list_iter = iter(my_list)

print(my_list_iter)  # 输出 <list_iterator object at 0x...>

迭代器

实现了 __iter__()__next__() 方法的对象

__iter__() 返回迭代器自身,而 __next__() 返回序列中的下一个值

如果没有值可返回,则会引发 StopIteration 异常

# 通过 iter() 函数获取迭代器
my_list_iter = iter(my_list)

# 使用 next() 函数获取下一个元素
print(next(my_list_iter))  # 输出 1
print(next(my_list_iter))  # 输出 2
print(next(my_list_iter))  # 输出 3

# 如果继续调用 next(),会引发 StopIteration 异常
try:
    print(next(my_list_iter))
except StopIteration:
    print("End of iterator")

自定义迭代器

  • 可迭代对象的 迭代器一旦被完全消费,状态不会重置

  • 每次需要重新迭代时,可以创建新的迭代器实例,或实现返回新迭代器实例的 __iter__ 方法

class MyIterator:
    def __init__(self, data):
        print("Initializing MyIterator")
        self.data = data
        self.index = 0

    def __iter__(self):
        print("Calling __iter__")
        return self

    def __next__(self):
        print("Calling __next__")
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        raise StopIteration

# 创建可迭代对象和迭代器
test_list = [1, 2, 3, 4]
my_iterable = MyIterator(test_list)

# 初始化完成,打印类型
print(type(test_list))     # <class 'list'>
print(type(my_iterable))   # <class '__main__.MyIterator'>

# 使用 for 循环遍历
for item in my_iterable:
    print("Iterating item:", item)

# TODO:再次使用for循环,就不执行了
for item in my_iterable:
	print("Inerating angin", item)

##################
# 使用 for 循环遍历
for item in MyIterator(test_list):
    print(item)

# 再次创建新的迭代器实例
for item in MyIterator(test_list):
    print(item)

区别和联系

  • 可迭代对象:可以用 iter() 函数获取迭代器的对象。常见的内置类型如列表、元组、字符串等都是可迭代对象。

  • 迭代器:实现了 __iter__()__next__() 方法的对象。迭代器本身也是可迭代对象,因为它实现了 __iter__() 方法并返回自身。

常用的内置函数和方法

  • iter(obj):获取对象的迭代器。

  • next(iterator[, default]):获取迭代器的下一个元素,如果迭代结束则返回默认值(如果提供)。

  • list()和tuple()中也可以传递可迭代对象

for循环的本质

  1. 先吊用iter()函数,它会自动调用可迭代对象中的__iter__方法,返回这个可迭代对象的 迭代器

  2. 对获取到的迭代器 不断调用next()函数,它会自动调用迭代器中的__next__方法来获取下一个值

  3. 但遇到StopInteration异常后,循环结束

生成器

生成器(Generator)是 Python 中的一种用于创建迭代器的简便方式,是一类特殊的迭代器

让你在迭代过程中产生值,而不需要一次性将所有值加载到内存中,大量数据或流式数据时特别有用(惰性求值)

基本概念

  • 生成器函数:使用 yield 关键字的函数,这个函数在调用时返回一个生成器对象。

  • 生成器表达式:类似于列表推导式,但使用小括号而不是方括号,返回一个生成器对象。

特点

  • 惰性求值:生成器按需生成值,可以节省内存。

  • 状态记忆:生成器在每次产生值后会记住其状态,下一次迭代会从上次停止的地方继续。

生成器函数

使用 yield 关键字的函数会返回一个生成器对象

每次调用生成器的 __next__() 方法时,生成器函数会运行到下一个 yield 语句,返回相应的值,并暂停执行,保存当前的执行状态。

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

# 使用生成器函数
fib = fibonacci(10)
for num in fib:
    print(num)

生成器表达式

生成器表达式使用类似列表推导式的语法,但用小括号包裹

它们返回一个生成器对象,可以像迭代器一样使用

# 创建一个生成器表达式,生成从0到9的平方数
squares = (x * x for x in range(10))

# 使用生成器
for square in squares:
    print(square)

send、next的区别

在启动生成器时,第一个方法调用必须是 nextsend(None),因为在生成器启动时没有 yield 语句可以接收值。

  • 功能不同

    • next 方法:恢复生成器执行到下一个 yield 语句,并返回该 yield 语句的值。

    • send 方法:向生成器发送一个值,并恢复生成器执行到下一个 yield 语句。发送的值成为上一个 yield 表达式的返回值。

  • 启动生成器

    • next 方法:可以用来启动生成器。

    • send 方法:必须以 send(None) 的形式来启动生成器。

  • 用法场景

    • next 方法:用于简单的值生成和迭代。

    • send 方法:用于需要向生成器内部传递数据的复杂交互。

def interactive_generator():
    print('Generator started')
    value = yield 1
    print(f'Received: {value}')
    value = yield 2
    print(f'Received: {value}')
    value = yield 3
    print(f'Received: {value}')

gen = interactive_generator()

# 启动生成器,必须使用 next 或 send(None)
print(next(gen))  # Generator started, 输出: 1

# 使用 send 向生成器发送数据,并继续执行到下一个 yield
print(gen.send('first'))  # Received: first, 输出: 2
print(gen.send('second'))  # Received: second, 输出: 3

# 生成器到此结束,再调用会引发 StopIteration 异常
# print(next(gen))  # 会引发 StopIteration 异常

高级用法

斐波那契数列

斐波那契数列:

# 返回列表
def fib_list(n):
    if n == 1:
        return [1]
    if n == 2:
        return [1, 1]
    fibs = [1, 1]
    for i in range(2, n):
        fibs.append(fibs[-1] + fibs[-2])
    return fibs


# yield实现
def fib_yield(n):
    if n == 1:
        yield 1
    if n == 2:
        yield 1
        yield 1
    fibs = [1, 1]
    for i in range(2, n):
        fibs.append(fibs[-1] + fibs[-2])
        yield fibs[-1]


# 递归实现
def fib_recursion(n):
    if n == 1 or n == 2:
        return 1
    return fib_recursion(n - 1) + fib_recursion(n - 2)

# 返回单个值
def fib(n):
    if n == 1 or n == 2:
        return 1
    return fib(n - 1) + fib(n - 2)


print(fib_list(10))

无限序列

生成器可以用于生成无限序列,只在需要时产生值。

def count(start=0, step=1):
    n = start
    while True:
        yield n
        n += step

# 使用无限生成器
counter = count(10, 2)

for _ in range(5):
    print(next(counter))

管道模式

生成器可以通过管道模式连接,用于数据流的逐步处理。

def integers():
    for i in range(1, 9):
        yield i

def squared(seq):
    for i in seq:
        yield i * i

def negated(seq):
    for i in seq:
        yield -i

# 使用管道模式
numbers = integers()
squared_numbers = squared(numbers)
negated_numbers = negated(squared_numbers)

for num in negated_numbers:
    print(num)

yield from

yield from 可以用于委托部分生成工作给另一个生成器。

def generator_a():
    yield 1
    yield 2

def generator_b():
    yield from generator_a()
    yield 3
    yield 4

# 使用生成器
for num in generator_b():
    print(num)

总结

  • 生成器函数:使用 yield 关键字定义,生成器函数每次暂停在 yield 语句处,返回值并保存状态

  • 生成器表达式:类似列表推导式,使用小括号,返回生成器对象

  • 优点:生成器惰性求值,节省内存,可以处理无限序列和管道数据流

  • 高级用法:无限序列、管道模式、`yield from` 用于简化生成器代码

  • 生成器是 Python 中处理大量数据和流式数据的强大工具,通过按需生成值来优化性能和内存使用

yield拓展

yield与协程(coroutines)有密切关系

协程是一种更为复杂和灵活的生成器,用于实现多任务协作和异步编程

Python 中,yieldyield from 可以用来创建简单的协程

yield 和 协程

  • 生成器(Generators)

    • 生成器是使用 yield 关键字定义的函数,它们每次调用时生成一个值并暂停执行,保留当前的执行状态

    • 生成器是协程的基础

  • 协程(Coroutines)

    • 协程是可以在执行过程中暂停并恢复的函数,允许在暂停点进行数据传输

    • 协程的一个显著特点是可以通过 yield 表达式接收外部发送的数据。

基本生成器示例

def simple_generator():
    yield 1
    yield 2
    yield 3

gen = simple_generator()

print(next(gen))  # 输出: 1
print(next(gen))  # 输出: 2
print(next(gen))  # 输出: 3

协程示例

协程不仅可以产出值,还可以接收值。下面是一个简单的协程示例:

def simple_coroutine():
    print('Coroutine started')
    x = yield
    print(f'Coroutine received: {x}')

coro = simple_coroutine()

print(next(coro))  # 启动协程,输出: Coroutine started
print(coro.send(42))  # 向协程发送数据,输出: Coroutine received: 42

在这个示例中,yield表达式不仅可以返回值,还可以通过 send 方法接收值

这个特性使协程能够在不同执行点之间交换数据。

yield from 和 子生成器

yield from 可以简化协程代码,使得一个生成器可以委派其部分操作给另一个生成器。

def generator_a():
    yield 1
    yield 2

def generator_b():
    yield from generator_a()
    yield 3
    yield 4

for value in generator_b():
    print(value)  # 输出: 1, 2, 3, 4

async/await

在 Python 3.5 引入了 asyncawait 关键字,用于创建和管理协程

这些关键字使得编写异步代码更加直观和强大

import asyncio

async def async_coroutine():
    print('Coroutine started')

    await asyncio.sleep(1)

    print('Coroutine ended')

# 运行协程
asyncio.run(async_coroutine())

yieldasync/await 的对比

  • yield:用于生成器和协程,适用于简单的协作多任务和数据流操作

  • async/await:用于现代协程,适用于异步编程和并发操作

总结

  • 生成器:使用 yield 创建,主要用于逐步生成值

  • 协程:使用 yieldyield from 创建,允许在执行过程中暂停和恢复,可以在暂停点交换数据

  • async/await:用于现代协程,提供更简洁的异步编程语法,适合处理 I/O 绑定和高并发任务

yield 是协程的基础,允许在执行过程中暂停和恢复

asyncawait 提供了更高层次的抽象,简化了异步编程模型

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区