python快速入门学习笔记(进阶篇)二十七:生成器

  • 原创
  • 作者:程序员三丰
  • 发布时间:2026-05-19 18:21
  • 浏览量:2
Python入门第二十七课,主要是学习了生成器,生成器通过yield实现惰性求值,按需生成数据,有效节省内存并支持高效迭代。

学前提示:请务必先阅读了解『迭代器』后,再阅读本篇文章。因为生成器对象本身就是一种特殊的迭代器。

两个概念

生成器函数:函数体中如果出现了yield关键字,那该函数是『生成器函数』。不管能否执行到yield所在的位置,只要函数中有yield,那该函数就是『生成器函数』。

生成器对象:调用『生成器函数』时,其函数体不会立刻执行,而是返回一个『生成器对象』。

def demo():
    print('demo函数开始执行了……')
    print(100)
    yield
    a = 200
    print(a)

d = demo()
print(d) # <generator object demo at 0x000001FC96545B40>

几个细节

写在『生成器函数』中的代码,需要通过『生成器对象』来执行:

❏ 调用『生成器对象』的__next__()方法,会让『生成器函数』中的代码开始执行。

❏ 当『生成器函数』中的代码开始执行后,遇到yield会“暂停”,并会记录“暂停”的位置。

❏ 后续调用__next__()方法时,都会从上一次“暂停”的位置,继续运行,直到再次遇到yield

❏ 遇到return会抛出StopIteration异常,并将return后面的表达式,作为异常信息。

yield后面所写的表达式,会作为本次__next__()方法的返回值。

def demo():
    print('demo函数开始执行了')
    print(100)
    yield '我是第一个yield所返回的数据'
    a = 200
    print(a)
    yield '我是第二个yield所返回的数据'
    b = 300
    print(b)
    return '生成器异常'

d = demo()
r1 = next(d)
print(r1)
r2 = next(d)
print(r2)
try:
    next(d)
except StopIteration as e:
    print(e)

# 运行结果:
# demo函数开始执行了
# 100
# 我是第一个yield所返回的数据
# 200
# 我是第二个yield所返回的数据
# 300
# 生成器异常

生成器对象是一种特殊的迭代器(本质是通过yield自动实现了迭代器协议)。

def demo():
    print('demo函数开始执行了')
    print(100)
    yield '我是第一个yield所返回的数据'
    a = 200
    print(a)
    yield '我是第二个yield所返回的数据'
    b = 300
    print(b)
    return '生成器异常'

d = demo()

# 验证:生成器对象d,和迭代器一样,也拥有:__iter__ 和 __next__ 方法
print(hasattr(d, '__iter__')) # True
print(hasattr(d, '__next__')) # True

# 验证:生成器对象的__iter__方法,和迭代器一样,返回的也是自身
result = iter(d)
print(result == d) # True

# for循环遍历生成器
for item in d:
    print(item)

# for循环背后的逻辑
d2 = demo()
gen = iter(d2)
while True:
    try:
        value = next(gen)
        print(value)
    except StopIteration:
        break

yield也能写在循环里。

def create_car(total):
    for index in range(1, total + 1):
        yield f'下线了第{index}台车'

# 创建迭代器对象
cars = create_car(5)
print(cars)

# 调用一次cars的__next__方法,就会得到一台车
c1 = next(cars)
print(c1)
c2 = next(cars)
print(c2)
c3 = next(cars)
print(c3)
c4 = next(cars)
print(c4)
c5 = next(cars)
print(c5)
# 第6次执行就会抛出StopIteration异常
# c6 = next(cars)
# print(c6)

# 使用for循环遍历
cars2 = create_car(5)
for car in cars2:
    print(car)

yield from能把一个『可迭代对象』里的内容依次yield出去。(替代:for循环 + yield

def demo():
    nums = [10, 20, 30, 40, 50]
    yield from nums

d = demo()
r1 = next(d)
print(r1)
r2 = next(d)
print(r2)
r3 = next(d)
print(r3)
r4 = next(d)
print(r4)
r5 = next(d)
print(r5)

d2 = demo()
for x in d2:
    print(x)

使用生成器.send(值)可以让生成器继续执行的同时,给上一次yield传值。

注意:

next只能取值,send既能取值,也能送值。

第一次启动生成器,不能传值!(或者说只能传None值)

def demo():
    print('demo函数开始执行了')
    print(100)
    a = yield '我是第一个yield所返回的数据'
    print(f'_ {a}')
    b = yield '我是第二个yield所返回的数据'
    print(f'_ {b}')
    return '生成器异常'

d = demo()
r1 = next(d)
print(r1)
r2 = d.send(666)
print(r2)
try:
    d.send(888)
except StopIteration as e:
    print(e)

# 运行结果:
# demo函数开始执行了
# 100
# 我是第一个yield所返回的数据
# _ 666
# 我是第二个yield所返回的数据
# _ 888
# 生成器异常

生成器的应用

➊ 用生成器实现遍历Person类的实例对象:

class Person:
    def __init__(self, name, age, gender, address):
        self.name = name
        self.age = age
        self.gender = gender
        self.address = address
        self.__attr = [name, age, gender, address]

    def __iter__(self):
        # yield self.name
        # yield self.age
        # yield self.gender
        # yield self.address
        yield from self.__attr

p1 = Person('远方', 41, '男', '山西太原')
for attr in p1:
    print(attr)

# 运行结果:
#远方
#41
#男
#山西太原

➋ 用生成器实现斐波那契数列:

def fibo(total):
    pre = 1
    cur = 1

    for index in range(total):
        if index < 2:
            yield 1
        else:
            value = pre + cur
            pre = cur
            cur = value
            yield value

f1 = fibo(10)
for item in f1:
    print(item)

# 运行结果:
# 1
# 1
# 2
# 3
# 5
# 8
# 13
# 21
# 34
# 55

无论是迭代器,还是生成器对象,都可以用listtupleset等直接拿到其里面的所有内容(注意:如果数据量很大,可能会挤爆内存)。

def fibo(total):
    pre = 1
    cur = 1

    for index in range(total):
        if index < 2:
            yield 1
        else:
            value = pre + cur
            pre = cur
            cur = value
            yield value

f1 = fibo(10)
result = tuple(f1)
print(result) # (1, 1, 2, 3, 5, 8, 13, 21, 34, 55)

生成器表达式

生成器表达式:一种用类似列表推导式的语法,快速创建生成器对象的方式。

语法格式:(表达式 for 变量 in 可迭代对象)

什么时候适合用生成器表达式?—— 当“每个结果,只依赖当前这一个元素”时。

nums = [10, 20, 30, 40, 50]

# 列表推导式
result1 = [n * 2 for n in nums]
print(result1)

# 生成器表达式和列表推导式很像,不要混淆
result2 = (n * 2 for n in nums)
print(result2)  # <generator object <genexpr> at 0x000002899789A8E0>

for n in nums:
    print(n)
声明:本文为原创文章,51blog.xyz和作者拥有版权,如需转载,请注明来源于51blog.xyz并保留原文链接:https://www.51blog.xyz/article/129.html

文章归档

推荐文章

buildadmin logo
Thinkphp8 Vue3 Element PLus TypeScript Vite Pinia

🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。

热门标签

PHP ThinkPHP ThinkPHP5.1 Go Mysql Mysql5.7 Redis Linux CentOS7 Git HTML CSS CSS3 Javascript JQuery Vue LayUI VMware Uniapp 微信小程序 docker wiki Confluence7 学习笔记 uView ES6 Ant Design Pro of Vue React ThinkPHP6.0 chrome 扩展 翻译工具 Nuxt SSR 服务端渲染 scrollreveal.js ThinkPHP8.0 Mac webman 跨域CORS vscode GitHub ECharts Canvas vue3 three.js 微信支付 PHP全栈开发 Python AI 人工智能 AI生成 工作经验 实战笔记