如何通过一篇文章了解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基于pygame实现图片代替鼠标移动效果
Nov 11 Python
Python基础学习之常见的内建函数整理
Sep 06 Python
使用django-crontab实现定时任务的示例
Feb 26 Python
解决nohup执行python程序log文件写入不及时的问题
Jan 14 Python
Python3.5基础之函数的定义与使用实例详解【参数、作用域、递归、重载等】
Apr 26 Python
对python中GUI,Label和Button的实例详解
Jun 27 Python
opencv resize图片为正方形尺寸的实现方法
Dec 26 Python
Python爬虫解析网页的4种方式实例及原理解析
Dec 30 Python
PyCharm中配置PySide2的图文教程
Jun 18 Python
Python正则表达式高级使用方法汇总
Jun 18 Python
Django-Scrapy生成后端json接口的方法示例
Oct 06 Python
基于Python编写简易版的天天跑酷游戏的示例代码
Mar 23 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添加Xdebug扩展的方法
2014/02/12 PHP
yii框架配置默认controller和action示例
2014/04/30 PHP
php smtp实现发送邮件功能
2017/06/22 PHP
用JavaScript脚本实现Web页面信息交互
2006/12/21 Javascript
js getElementsByTagName的简写方式
2010/06/27 Javascript
Jquery ajax传递复杂参数给WebService的实现代码
2011/08/08 Javascript
JavaScript 创建运动框架的实现代码
2013/05/08 Javascript
jquery处理json数据实例分析
2014/06/03 Javascript
JavaScript实现从数组中选出和等于固定值的n个数
2014/09/03 Javascript
轻松创建nodejs服务器(4):路由
2014/12/18 NodeJs
JavaScript 弹出子窗体并返回结果到父窗体的实现代码
2016/05/28 Javascript
JS构造函数与原型prototype的区别介绍
2016/07/04 Javascript
详解Chai.js断言库API中文文档
2018/01/31 Javascript
微信小程序实现左滑修改、删除功能
2020/10/19 Javascript
微信小程序实现登录注册tab切换效果
2020/12/29 Javascript
微信小程序自定义底部导航带跳转功能
2018/11/27 Javascript
Layui带搜索的下拉框的使用以及动态数据绑定方法
2019/09/28 Javascript
JS数组进阶示例【数组的几种函数用法】
2020/01/16 Javascript
python利用datetime模块计算时间差
2015/08/04 Python
python的dataframe转换为多维矩阵的方法
2018/04/11 Python
python学习之hook钩子的原理和使用
2018/10/25 Python
解决python opencv无法显示图片的问题
2018/10/28 Python
python 读取竖线分隔符的文本方法
2018/12/20 Python
Python for循环与range函数的使用详解
2019/03/23 Python
CentOS7安装Python3的教程详解
2019/04/10 Python
Django+uni-app实现数据通信中的请求跨域的示例代码
2019/10/12 Python
python通过实例讲解反射机制
2019/10/17 Python
PyQT5 emit 和 connect的用法详解
2019/12/13 Python
Uber Eats台湾:寻找附近提供送餐服务的餐厅
2018/05/07 全球购物
英国设计的甲板鞋和船鞋:Chatham
2018/12/06 全球购物
机械设计专业应届生求职信
2013/11/21 职场文书
搞笑获奖感言
2014/01/30 职场文书
环境建设实施方案
2014/03/14 职场文书
党员政治学习材料
2014/05/14 职场文书
工作试用期自我评价
2015/03/10 职场文书
运动会开幕式主持词
2015/07/01 职场文书