如何通过一篇文章了解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实现搜索本地文件信息写入文件的方法
Feb 22 Python
django开发教程之利用缓存文件进行页面缓存的方法
Nov 10 Python
python实现windows下文件备份脚本
May 27 Python
python实现排序算法解析
Sep 08 Python
如何通过python的fabric包完成代码上传部署
Jul 29 Python
Python元组 tuple的概念与基本操作详解【定义、创建、访问、计数、推导式等】
Oct 30 Python
python3+selenium获取页面加载的所有静态资源文件链接操作
May 04 Python
python如何更新包
Jun 11 Python
Python虚拟环境的创建和包下载过程分析
Jun 19 Python
Pycharm自带Git实现版本管理的方法步骤
Sep 18 Python
用python批量下载apk
Dec 29 Python
Python集合set()使用的方法详解
Mar 18 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循环语句 for()与foreach()用法区别介绍
2012/09/05 PHP
利用PHP+JS实现搜索自动提示(实例)
2013/06/09 PHP
使用PHP编写的SVN类
2013/07/18 PHP
phpMyAdmin自动登录和取消自动登录的配置方法
2014/05/12 PHP
ThinkPHP在Cli模式下使用模板引擎的方法
2015/09/25 PHP
PHP开发之归档格式phar文件概念与用法详解【创建,使用,解包还原提取】
2017/11/17 PHP
Laravel 加载第三方类库的方法
2018/04/20 PHP
PHP简单实现记录网站访问量功能示例
2018/06/06 PHP
基于jquery的划词搜索实现(备忘)
2010/09/14 Javascript
extjs实现选择多表自定义查询功能 前台部分(ext源码)
2011/12/20 Javascript
jQuery表单验证插件formValidator(改进版)
2012/02/03 Javascript
JS对img进行操作(换图片/切图/轮换/停止)
2013/04/17 Javascript
js确认删除对话框效果的示例代码
2014/02/20 Javascript
ExtJS4 表格的嵌套 rowExpander应用
2014/05/02 Javascript
完美解决js传递参数中加号和&amp;号自动改变的方法
2016/10/11 Javascript
Vue.js实现文章评论和回复评论功能
2020/05/30 Javascript
Web制作验证码功能实例代码
2017/06/19 Javascript
浅析node.js的模块加载机制
2018/05/25 Javascript
小程序实现留言板
2018/11/02 Javascript
JS实现指定区域的全屏显示功能示例
2019/04/25 Javascript
浅谈python中的变量默认是什么类型
2016/09/11 Python
Python中的id()函数指的什么
2017/10/17 Python
Numpy中stack(),hstack(),vstack()函数用法介绍及实例
2018/01/09 Python
python正则表达式匹配不包含某几个字符的字符串方法
2019/07/23 Python
PyQt5 界面显示无响应的实现
2020/03/26 Python
python如何调用java类
2020/07/05 Python
html5中为audio标签增加停止按钮动作实现方法
2013/01/04 HTML / CSS
会计岗位职责
2013/11/08 职场文书
自主招生自荐信指南
2014/02/04 职场文书
早会主持词
2014/03/17 职场文书
《生命的药方》教学反思
2014/04/08 职场文书
住宅使用说明书
2014/05/09 职场文书
入党综合考察材料
2014/06/02 职场文书
开幕式邀请函
2015/01/31 职场文书
MySQL定时备份数据库(全库备份)的实现
2021/09/25 MySQL
MySQL数据库查询进阶之多表查询详解
2022/04/08 MySQL