如何通过一篇文章了解Python中的生成器


Posted in Python onApril 02, 2022

前言

生成器很容易实现,但却不容易理解。生成器也可用于创建迭代器,但生成器可以用于一次返回一个可迭代的集合中一个元素。现在来看一个例子:

def yrange(n):
    i = 0
    while i < n:
        yield i
        i += 1

每次执行 yield 语句时,函数都会生成一个新值。

如何通过一篇文章了解Python中的生成器

“生成器”这个词被混淆地用来表示生成的函数和它生成的内容。 

当调用生成器函数时,它甚至没有开始执行该函数就返回一个生成器对象。 当第一次调用 next() 方法时,函数开始执行直到它到达 yield 语句。 产生的值由下一次调用返回。

以下示例演示了 yield 和对生成器对象上的 next 方法的调用之间的相互作用。

>>> def foo():
...     print("begin")
...     for i in range(3):
...         print("before yield", i)
...         yield i
...         print("after yield", i)
...     print("end")
...
>>> f = foo()
>>> next(f)
begin
before yield 0
0
>>> next(f)
after yield 0
before yield 1
1
>>> next(f)
after yield 1
before yield 2
2
>>> next(f)
after yield 2
end
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    next(f)
StopIteration
>>>

生成器也是迭代器

生成器也是迭代器,支持使用 for 循环。当使用 for 语句开始对一组项目进行迭代时,即运行生成器。一旦生成器的函数代码到达 yield 语句,生成器就会将其执行交还给 for 循环,从集合中返回一个新值。生成器函数可以根据需要生成任意数量的值(可能是无限的),依次生成每个值。

f_2 = foo()
for i in f_2: print(i)

begin
before yield 0
0
after yield 0
end
before yield 1
1
after yield 1
end
before yield 2
2
after yield 2
end

如何通过一篇文章了解Python中的生成器

当一个函数包含 yield 时,Python 会自动实现一个迭代器,为我们应用所有需要的方法,比如 __iter__() 和 __next__(),所以生成器也能和迭代器有相同的功能,如下所示:

def yrange():
    i = 1
    while True:
        yield i
        i = i + 1

def squares():
    for i in yrange():
        yield i * i

def take(n, seq):
    seq = iter(seq)
    result = []
    try:
        for i in range(n):
            result.append(next(seq))
    except StopIteration:
        pass
    return result

print(take(5, squares()))

# [1, 4, 9, 16, 25]

接下来看一下如何使用生成器计算斐波那契数列:

def fib(n):
    if n <= 1:
        return 1
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
        yield a

for i in fib(10):
    print(i, end=' ')

# Result:1 1 2 3 5 8 13 21 34 55

生成器推导式

生成器表达式是列表推导式的生成器版本。它们看起来像列表推导式,但返回的是一个生成器,而不是一个列表。生成器推导式的本质:

  • 使用 yield 会产生一个生成器对象
  • 用 return 将返回当前的第一个值。
generator_expressions = (x for x in range(10))
generator_expressions
<generator object <genexpr> at 0x0000023F8BC51AF0>
sum(generator_expressions)
45

无限生成器

生成器的另一个常见场景是无限序列生成。在 Python 中,当您使用有限序列时,您可以简单地调用 range() 并在列表中对其进行计数,例如:

a = range(5)
print(list(a))
[0, 1, 2, 3, 4]

也可以这样做,使用如下生成器生成无限序列:

def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1

运行此代码时,可以看到其运行非常快,可以通过 CTRL+C 来使得程序结束,如下:

生成器实际用法

1. 读取文件行

生成器的一个常见用法是处理大型文件或数据流,例如 CSV 文件。假设我们需要计算文本文件中有多少行,我们的代码可能如下所示:

def csv_reader(file_name):
    file = open(file_name)
    result = file.read().split("\n")
    return result

csv_gen = csv_reader("some_file.csv")
row_count = 0

for row in csv_gen:
    row_count += 1

print(f"Row count is {row_count}")

我们的 csv_reader 函数将简单地将文件打开到内存中并读取所有行,然后它将行拆分并与文件数据形成一个数组。如果文件包含几千行,可能就会导致速度变慢,设置是内存被占满。

这里就可以通过生成器重构的 csv_reader 函数。

def csv_reader(file_name):
    for row in open(file_name, "r"):
        yield row

2.读取文件内容

def readfiles(filenames):
    for f in filenames:
        for line in open(f):
            yield line

def grep(pattern, lines):
    return (line for line in lines if pattern in line)

def printlines(lines):
    for line in lines:
        print(line, end="")

def main(pattern, filenames):
    lines = readfiles(filenames)
    lines = grep(pattern, lines)
    printlines(lines)

高级生成器用法

到目前为止,我们已经介绍了生成器最常见的用途和构造,但还有更多内容需要介绍。随着时间的推移,Python 为生成器添加了一些额外的方法:

  • send() 函数
  • throw() 函数
  • close() 函数

接下来,我们来看一下如何使用这三个函数。

首先,新建一个生成器将生成素数,其实现如下:

def isPrime(n):
    if n < 2 or n % 1 > 0:
        return False
    elif n == 2 or n == 3:
        return True
    for x in range(2, int(n**0.5) + 1):
        if n % x == 0:
            return False
    return True

def getPrimes():
    value = 0
    while True:
        if isPrime(value):
            i = yield value
            if i is not None:
                value = i
        value += 1

然后我们调用 send() 函数,这个函数会向生成器 prime_gen 传入一个值,然后从这个值开始计算下一个素数的值:

prime_gen = getPrimes()
print(next(prime_gen))
print(prime_gen.send(1000))
print(next(prime_gen))

可以看到如下结果:

如何通过一篇文章了解Python中的生成器

throw() 允许您使用生成器抛出异常。例如,这对于以某个值结束迭代很有用。比如我们想得到小于 20 的素数就可以使用如下方法:

prime_gen = getPrimes()

for x in prime_gen:
    if x > 20:
        prime_gen.throw(ValueError, "I think it was enough!")
    print(x)

运行该代码,得到结果如下:

如何通过一篇文章了解Python中的生成器

在前面的示例中,我们通过引发异常来停止迭代,但这并不是用户想看到的,谁想看到报错呢。因此,结束迭代的更好方法是使用 close():

prime_gen = getPrimes()

for x in prime_gen:
    if x > 20:
        prime_gen.close()
    print(x)

运行结果如下图:

如何通过一篇文章了解Python中的生成器

可以看到,生成器在运行到停止了,没有引发任何异常。

总结

生成器简化了迭代器的创建。 生成器是产生一系列结果而不是单个值的函数。

生成器可以用于优化 Python 应用程序的性能,尤其是在使用大型数据集或文件时的场景中。

生成器还通过避免复杂的迭代器实现或通过其他方式处理数据来提供清晰的代码。

参考链接:

How to Use Generator and yield in Python

https://realpython.com/introduction-to-python-generators/

https://anandology.com/python-practice-book/iterators.html

到此这篇关于Python中生成器的文章就介绍到这了,更多相关Python的生成器内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python中的index()方法使用教程
May 18 Python
python使用urllib2提交http post请求的方法
May 26 Python
Python数据类型详解(一)字符串
May 08 Python
简单谈谈Python中的几种常见的数据类型
Feb 10 Python
深入浅出学习python装饰器
Sep 29 Python
Python实现的自定义多线程多进程类示例
Mar 23 Python
Python实战购物车项目的实现参考
Feb 20 Python
python实现微信小程序用户登录、模板推送
Aug 28 Python
Python文件操作方法详解
Feb 09 Python
用Python在Excel里画出蒙娜丽莎的方法示例
Apr 28 Python
使用OpenCV获取图片连通域数量,并用不同颜色标记函
Jun 04 Python
Python自动化办公Excel模块openpyxl原理及用法解析
Nov 05 Python
Python pyecharts绘制条形图详解
Python OpenCV超详细讲解读取图像视频和网络摄像头
基于Python实现股票收益率分析
python实现对doc、txt、xls等文档的读写操作
Apr 02 #Python
Python OpenCV超详细讲解基本功能
python函数的两种嵌套方法使用
Apr 02 #Python
Python OpenCV超详细讲解调整大小与图像操作的实现
You might like
PHP网页游戏学习之Xnova(ogame)源码解读(九)
2014/06/24 PHP
PHP中一些可以替代正则表达式函数的字符串操作函数
2014/11/17 PHP
php和editplus正则表达式去除空白行
2015/04/17 PHP
YII Framework框架教程之国际化实现方法
2016/03/14 PHP
PHP使用FFmpeg获取视频播放总时长与码率等信息
2016/09/13 PHP
Yii框架弹出框功能示例
2017/01/07 PHP
PHP实现找出链表中环的入口节点
2018/01/16 PHP
php对象工厂类完整示例
2018/08/09 PHP
PHP设计模式(三)建造者模式Builder实例详解【创建型】
2020/05/02 PHP
Javascript 中 null、NaN和undefined的区别总结
2013/04/10 Javascript
EXTjs4.0的store的findRecord的BUG演示代码
2013/06/08 Javascript
jquery中map函数与each函数的区别实例介绍
2014/06/23 Javascript
JS实现的用来对比两个用指定分隔符分割的字符串是否相同
2014/09/19 Javascript
Nodejs中读取中文文件编码问题、发送邮件和定时任务实例
2015/01/01 NodeJs
理解javascript闭包
2015/12/15 Javascript
js replace(a,b)之替换字符串中所有指定字符的方法
2016/08/17 Javascript
jquery操作checkbox火狐下第二次无法勾选的解决方法
2016/10/10 Javascript
JS动态生成年份和月份实例代码
2017/02/04 Javascript
javascript实现文字无缝滚动效果
2017/08/26 Javascript
vue.js将时间戳转化为日期格式的实现代码
2018/06/05 Javascript
JS三级联动代码格式实例详解
2019/12/30 Javascript
sharp.js安装过程中遇到的问题总结
2020/04/02 Javascript
Django中实现一个高性能计数器(Counter)实例
2014/07/09 Python
python创建临时文件夹的方法
2015/07/06 Python
Python中浅拷贝copy与深拷贝deepcopy的简单理解
2018/10/26 Python
Python基于smtplib协议实现发送邮件
2020/06/03 Python
美国知名眼镜网站:Target Optical
2020/04/04 全球购物
北京华建集团SQL面试题
2014/06/03 面试题
高中军训广播稿
2014/01/14 职场文书
电大毕业生自我鉴定
2014/04/10 职场文书
语文课外活动总结
2014/08/27 职场文书
介绍信格式
2015/01/30 职场文书
城南旧事电影观后感
2015/06/16 职场文书
民主生活会主持词
2015/07/01 职场文书
详解Python如何批量采集京东商品数据流程
2022/01/22 Python
mysql的单列多值存储实例详解
2022/04/05 MySQL