当前位置:首页>python>Python之生成器

Python之生成器

  • 2026-09-05 21:04:03
Python之生成器

第九章 Python之生成器

生成器概念

  1. 生成器函数:函数体中如果出现了yield关键字,那么该函数就叫做生成器函数。
  2. 生成器对象:调用生成器函数时,其函数体不会立即执行,而是返回一个生成器对象。

备注:不管能否执行到yield所在的位置,只要有yield,那该函数就是生成器函数。

def test():
    print('test函数开始执行...')
    print(100)
    yield
    a = 200
    print(a)

t = test()
print(t)

## 执行结果:
<generator object test at 0x000001F3262B1F50>

## 代码解释:体会上面的概念,此处为什么执行结果返回的是一个生成器对象,而没有打印函数内部的print语句结果。

生成器细节

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

  1. 调用生成器对象的__next__方法,会让生成器函数中的代码开始执行。
  2. 当生成器函数中的代码开始执行后,遇到yield会暂停,并会记录暂停的位置。
  3. 后续调用__next__方法时,都会从上一次暂停的位置,继续运行,直到再次遇见yield。
  4. yield后面所写的表达式,会作为本次__next__方法的返回值。
def test():
    print('test函数开始执行了...')
    print(100)
    yield '我是第一个yield所返回的数据'
    a = 200
    print(a)
    yield '我是第二个yield所返回的数据'
    b = 300
    print(b)
    return 'hello yield'

t = test()
t1 = next(t)
print(t1)
t2 = next(t)
print(t2)
try:
    next(t)
except StopIteration as e:
    print(e)

## 执行结果:
test函数开始执行了...
100
我是第一个yield所返回的数据
200
我是第二个yield所返回的数据
300
hello yield

## 代码解释:最后一个next(t)如果不适用异常机制捕获,会执行破异常,那么return的值不会返回,此处得到hello yield其实是异常捕获的结果。

生成器对象其实就是一种特殊的迭代器(本质是通过yield自动实现了迭代器协议,上一篇讲过迭代器,忘记的同学可以看上一篇)

def test():
    print('test函数开始执行了...')
    print(100)
    yield '我是第一个yield所返回的数据'
    a = 200
    print(a)
    yield '我是第二个yield所返回的数据'
    b = 300
    print(b)
    return 'hello yield'

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

## 执行结果如下:
True
True
def test():
    print('test函数开始执行了...')
    print(100)
    yield '我是第一个yield所返回的数据'
    a = 200
    print(a)
    yield '我是第二个yield所返回的数据'
    b = 300
    print(b)
    return 'hello yield'

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

## 执行结果如下:
True
def test():
    print('test函数开始执行了...')
    print(100)
    yield '我是第一个yield所返回的数据'
    a = 200
    print(a)
    yield '我是第二个yield所返回的数据'
    b = 300
    print(b)
    return 'hello yield'

t = test()

# 模拟分析:for循环背后的逻辑
gen = iter(t)
while True:
    try:
        value = next(gen)
        print(value)
    except StopIteration:
        break

## 执行结果如下:
test函数开始执行了...
100
我是第一个yield所返回的数据
200
我是第二个yield所返回的数据
300

yield也可以写在循环里面

def create_compary(total):
    for index in range(1, total + 1):
        yield f'我是第{index}家公司'

# companys是生成器对象
companys = create_compary(5)
# 调用一次companys的__next__方法,就会得到一家公司
c1 = next(companys)
print(c1)
c2 = next(companys)
print(c2)
c3 = next(companys)
print(c3)
c4 = next(companys)
print(c4)
c5 = next(companys)
print(c5)

## 执行结果如下:
我是第1家公司
我是第2家公司
我是第3家公司
我是第4家公司
我是第5家公司

for company in companys:
    print(company)

## 执行结果如下:
我是第1家公司
我是第2家公司
我是第3家公司
我是第4家公司
我是第5家公司

## 解释:直接执行for循环是没有结果的,迭代器中讲过,迭代器是会被消耗的,上面已经调用了5次next方法,如果要让for循环打印公司,需要注释掉上面的5次next执行。

yield from能把一个可迭代对象里的东西依次yield出来,可以替代for + yield。

def test():
    nums = [100,200,300,400]
    yield from nums

t = test()
t1 = next(t)
print(t1)
t2 = next(t)
print(t2)
t3 = next(t)
print(t3)
t4 = next(t)
print(t4)

## 执行结果如下:
100
200
300
400

# 和下面的for循环式一个样的
for item in t:
    print(item)

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

  1. next只能取值,send既能取值,也能传值。
  2. 第一次启动生成器时,不能传值。(或者说只能传递None值)。
def test():
    print('test函数开始执行了...')
    print(100)
    a = yield '我是第一个yield所返回的数据'
    print(a)
    b = yield '我是第二个yield所返回的数据'
    print(b)
    return 'hello yield'

t = test()
t1 = t.send(None)
print(t1)
t2 = t.send('这是传给a的值')
print(t2)
try:
    t.send('这是传给b的值')
except StopIteration as e:
    print(e)

# 执行结果如下:
test函数开始执行了...
100
我是第一个yield所返回的数据
这是传给a的值
我是第二个yield所返回的数据
这是传给b的值
hello yield

生成器的应用

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

# 用生成器实现遍历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('孙悟空', 99 , '猴', '花果山水帘洞')
for attr in p1:
    print(attr)

# 执行结果如下:
孙悟空
99
猴
花果山水帘洞

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

# 用生成器实现斐波那契
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, end=' ')

# 执行结果如下:1 1 2 3 5 8 13 21 34 55 

无论是迭代器,还是生成器对象,都可以使用list,tuple,set等直接拿到其里面的所有内容。

注意:如果数据量很大,可能挤爆内存,造成内存溢出。

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 = set(f1)
print(result)

# 执行结果如下:
{1, 2, 3, 34, 5, 8, 13, 21, 55}

生成器表达式

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

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

什么时候适合用生成器表达式?

答:当每个结果,只依赖当前这一个元素时。

nums = [100, 200, 300, 400]

# 列表推导式
res1 = [n * 2 for n in nums]
print(res1) # [200, 400, 600, 800]

# 生成器表达式
res2 = [n * 2 for n in nums]
for item in res2:
    print(item, end=" ") # 200 400 600 800 

# 代码解释:生成器表达式,细细体会当‘每个结果,只依赖当前这一个元素时’这句话。此处的n就可以理解作用于nums中当前的每个元素,且只依赖于nums当前的这一个元素。

最新文章

随机文章