如何通过一篇文章了解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生成器的使用方法
Nov 21 Python
Python中用format函数格式化字符串的用法
Apr 08 Python
从Python的源码来解析Python下的freeblock
May 11 Python
python获得一个月有多少天的方法
Jun 04 Python
apache部署python程序出现503错误的解决方法
Jul 24 Python
python获取指定字符串中重复模式最高的字符串方法
Jun 29 Python
pycharm中使用anaconda部署python环境的方法步骤
Dec 19 Python
keras 特征图可视化实例(中间层)
Jan 24 Python
python shapely.geometry.polygon任意两个四边形的IOU计算实例
Apr 12 Python
opencv 图像礼帽和图像黑帽的实现
Jul 07 Python
Python如何读写CSV文件
Aug 13 Python
Python代码风格与编程习惯重要吗?
Jun 03 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开发中常用的三个表单验证函数使用小结
2010/03/03 PHP
php中的路径问题与set_include_path使用介绍
2014/02/11 PHP
ThinkPHP自动填充实现无限级分类的方法
2014/08/22 PHP
分享PHP计算两个日期相差天数的代码
2015/12/23 PHP
JavaScript获取onclick、onchange等事件值的代码
2013/07/22 Javascript
Jquery 数组操作大全个人总结
2013/11/13 Javascript
网页右下角弹出窗体实现代码
2014/06/05 Javascript
JavaScript判断变量是对象还是数组的方法
2014/08/28 Javascript
js实现鼠标感应向下滑动隐藏菜单的方法
2015/02/20 Javascript
ubuntu下安装nodejs以及升级的办法
2015/05/08 NodeJs
JavaScript中split与join函数的进阶使用技巧
2016/05/03 Javascript
有关jQuery中parent()和siblings()的小问题
2016/06/01 Javascript
jQuery的deferred对象使用详解
2016/09/25 Javascript
细数JavaScript 一个等号,两个等号,三个等号的区别
2016/10/09 Javascript
JS验证全角与半角及相互转化的介绍
2017/05/18 Javascript
Bootstrap Table 在指定列中添加下拉框控件并获取所选值
2017/07/31 Javascript
解决JQuery全选/反选第二次失效的问题
2017/10/11 jQuery
vue-cli项目优化方法- 缩短首屏加载时间
2018/04/01 Javascript
详解VUE中常用的几种import(模块、文件)引入方式
2018/07/03 Javascript
AngularJs的UI组件ui-Bootstrap之Tooltip和Popover
2018/07/13 Javascript
React key值的作用和使用详解
2018/08/23 Javascript
json数据格式常见操作示例
2019/06/13 Javascript
微信小程序实现消息框弹出动画
2020/04/18 Javascript
关于vue表单提交防双/多击的例子
2019/10/31 Javascript
[47:02]2018DOTA2亚洲邀请赛3月29日 小组赛B组 VP VS paiN
2018/03/30 DOTA
Python设计模式中单例模式的实现及在Tornado中的应用
2016/03/02 Python
django的settings中设置中文支持的实现
2019/04/28 Python
python使用HTMLTestRunner导出饼图分析报告的方法
2019/12/30 Python
解决python replace函数替换无效问题
2020/01/18 Python
python 使用openpyxl读取excel数据
2021/02/18 Python
css3实例教程 一款纯css3实现的发光屏幕旋转特效
2014/12/07 HTML / CSS
世界上最大的乐谱选择:Sheet Music Plus
2020/01/18 全球购物
大学应届毕业生个人求职信
2013/09/23 职场文书
优秀大学生推荐信范文
2013/11/28 职场文书
工程索赔意向书
2014/08/30 职场文书
2016先进集体事迹材料范文
2016/02/25 职场文书