Python 生成器

什么是生成器 - generator

生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,
不过这种迭代器更加优雅。

它不需要手动编写__iter__()和__next__()方法,只需要一个yiled关键字。

• 生成器一定是迭代器(反之不成立)。

• 因此任何生成器也是以一种懒加载的模式生成值。

生成器表达式

• 使用 ( ) 生成 generator, 将俩表推导式的 [ ] 改成 ( ) 即可得到生成器。

• 列表推导式与生成器表达式,定义和使用非常类似。但是,在(大量数据处理时)性能上相
差很大。

a = (x for x in range(10**9))
print(a)
print(dir(a))
print(next(a))
print(next(a))
print(next(a))

# <generator object <genexpr> at 0x000001E405F16C80>
# ['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
# 0
# 1
# 2

yield关键字

包含yield表达式的函数是特殊的函数,叫做生成器函数(generator function),被调用时将返回一个
迭代器(iterator),调用时可以使用next或send(msg)。

• 一个生成器中可以有多个yield。

• 一旦遇到yield,就会保存当前状态,然后返回yield后面的值。

• 当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。

• 当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。

• yield关键字:保留中间算法,下次继续执行。

def ger_content():
    x = 8
    yield x-1
    y = 6
    yield y+2
    z = 3
    yield z
g = ger_content()
print(type(g))
print(next(g))  # 第一次获取数据完了后会退出函数,保留上次执行的位置
print(next(g))  # 第二次再次执行的时候会从上一次执行的位置再次执行
print(next(g))

# <class 'generator'>
# 7
# 8
# 3

yield生成器来实现斐波那契数列

from itertools import islice
def fib():
    prev, curr = 0, 1
    while True:
        yield curr
        prev, curr = curr, curr + prev

f = fib()
print(list(islice(f, 0, 10)))

# [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

生成器 fib() 的执行过程:

• 当执行f=fib()返回的是一个生成器对象,此时函数体中的代码并不会执行,而是首先返回一个
iterable 对象!

• 只有显示或隐示地调用next的时候才会真正执行函数里面的代码。

• 执行到语句 yield b 时,fab() 函数会返回yield后面(右边)的值,并记住当前执行的状态。

• 下次调用next时,程序流会回到 yield b 的下一条语句继续执行。

• 看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返
回当前的迭代值。

• 由此可以看出,生成器通过关键字 yield 不断的将迭代器返回到内存进行处理,而不会一次性
的将对象全部放入内存,从而节省内存空间。

send数据

除了可以使用 next() 方法来获取下一个生成的值,用户还可以使用 send() 方法将一个
新的或者是被修改的值返回给生成器。除此之外,还可以使用 close() 方法来随时退出
生成器。

def counter(start_at=0):
    count = start_at
    while True:
        val = (yield count) # send 发送数据赋值给val,next获取时,val值为None
        if val is not None:
            count = val
        else:
            count += 1
count = counter(5)
print(type(count))
# generator
print(count.__next__())
print(count.send(9))  # 返回一个新的值给生成器中的yield count
print(count.__next__())
count.close()       # 关闭一个生成器
print(count.__next__())

# <class 'generator'>
# 5
# 9
# 10

yield from

def g1(x):
    yield range(x)
def g2(x):
    yield from range(x)
it1 = g1(5)
it2 = g2(5)
print( [ x for x in it1] )
#out [range(0, 5)]
print( [ x for x in it2] )
#out [0, 1, 2, 3, 4]

生成器的应用

当你需要以迭代的方式去处理一个巨大的数据集合。比如:一个巨大的文件/一个复杂的数据库查
询等。

如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的
缓冲区来不断读取文件的部分内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松
实现文件读取。

def read_file(fpath):
    BLOCK_SIZE = 1024
    with open(fpath, "rb") as f:
        while True:
            block = f.read(BLOCK_SIZE)
            if block:
                yield block
            else:
                return 

生成器的优点

生成器在Python中是一个非常强大的编程结构:

• 可以用更少地中间变量写流式代码。

• 相比其它容器对象它更能节省内存。

• 可以用更少的代码来实现相似的功能。

重写代码

凡看到类似:

def something():
    result = []
    for ... in ...:
        result.append(x)
    return result

都可以根据实际需求用生成器函数来替换:

def iter_something():
    for ... in ...:
        yield x

可迭代对象、迭代器、生成器

可迭代对象:实现了__iter__, 返回了一个迭代器对象。

迭代器:Python中一个实现了_iter_方法和_next_方法的类对象,就是迭代器

生成器:延迟操作。也就是在需要的时候才产生结果,不是立即产生结果。

• 生成器是只能遍历一次的。

• 生成器是一类特殊的迭代器。

三种关系小结

• 对象是可迭代对象, 但却不一定是迭代器。

• 如果对象属于迭代器, 那么这个对象一定是可迭代的。

• 迭代器实现了一个__next__()方法,可以通过next函数每次取出迭代器中的每个元素。

• 迭代器对象:__iter__()返回的是迭代器对象自身。

• 生成器是一种特殊的迭代器。

Vivado2023是一款集成开发环境软件,用于设计和验证FPGA(现场可编程门阵列)和可编程逻辑器件。对于使用Vivado2023的用户来说,license是必不可少的。 Vivado2023的license是一种许可证,用于授权用户合法使用该软件。许可证分为多种类型,包括评估许可证、开发许可证和节点许可证等。每种许可证都有不同的使用条件和功能。 评估许可证是免费提供的,让用户可以在一段时间内试用Vivado2023的全部功能。用户可以使用这个许可证来了解软件的性能和特点,对于初学者和小规模项目来说是一个很好的选择。但是,使用评估许可证的用户在使用期限过后需要购买正式的许可证才能继续使用软件。 开发许可证是付费的,可以永久使用Vivado2023的全部功能。这种许可证适用于需要长期使用Vivado2023进行开发的用户,通常是专业的FPGA设计师或工程师。购买开发许可证可以享受Vivado2023的技术支持和更新服务,确保软件始终保持最新的版本和功能。 节点许可证是用于多设备或分布式设计的许可证,可以在多个计算机上安装Vivado2023,并共享使用。节点许可证适用于大规模项目或需要多个处理节点进行设计的用户,可以提高工作效率和资源利用率。 总之,Vivado2023 license是用户在使用Vivado2023时必须考虑的问题。用户可以根据自己的需求选择合适的许可证类型,以便获取最佳的软件使用体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

韩未零

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值