如何通过一篇文章了解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根据开头和结尾字符串获取中间字符串的方法
Mar 26 Python
python使用arp欺骗伪造网关的方法
Apr 24 Python
Python 判断是否为质数或素数的实例
Oct 30 Python
Django rest framework工具包简单用法示例
Jul 20 Python
Python调用百度根据经纬度查询地址的示例代码
Jul 07 Python
Django ORM 聚合查询和分组查询实现详解
Aug 09 Python
Python基于Tensor FLow的图像处理操作详解
Jan 15 Python
Pycharm打开已有项目配置python环境的方法
Jul 03 Python
Python Charles抓包配置实现流程图解
Sep 29 Python
怎么解决pycharm license Acti的方法
Oct 28 Python
PyTorch预训练Bert模型的示例
Nov 17 Python
详解Pycharm第三方库的安装及使用方法
Dec 29 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+AJAX聊天程序[聊天室]提供下载
2007/07/21 PHP
linux iconv方法的使用
2011/10/01 PHP
比较strtr, str_replace和preg_replace三个函数的效率
2013/06/26 PHP
PHP中使用xmlreader读取xml数据示例
2014/12/29 PHP
WordPress网站性能优化指南
2015/11/18 PHP
php实现微信公众平台发红包功能
2018/06/14 PHP
php模式设计之观察者模式应用实例分析
2019/09/25 PHP
javascript模仿msgbox提示效果代码
2008/06/10 Javascript
从URL中提取参数与将对象转换为URL查询参数的实现代码
2012/01/12 Javascript
jQuery xml字符串的解析、读取及查找方法
2016/03/01 Javascript
AngularJS入门(用ng-repeat指令实现循环输出
2016/05/05 Javascript
jquery.qtip提示信息插件用法简单实例
2016/06/17 Javascript
微信小程序 后台https域名绑定和免费的https证书申请详解
2016/11/10 Javascript
详解Sea.js中Module.exports和exports的区别
2017/02/12 Javascript
jquery实现超简单的瀑布流布局【推荐】
2017/03/08 Javascript
Bootstrap DateTime Picker日历控件简单应用
2017/03/25 Javascript
详解Node.js 命令行程序开发教程
2017/06/07 Javascript
JS实现unicode和UTF-8之间的互相转换互转
2017/07/05 Javascript
利用纯JS实现像素逐渐显示的方法示例
2017/08/14 Javascript
NW.js 简介与使用方法
2018/02/01 Javascript
vue element-ui el-date-picker限制选择时间为当天之前的代码
2019/11/07 Javascript
JavaScript Reflect Metadata实现详解
2019/12/12 Javascript
Flexible.js可伸缩布局实现方法详解
2020/11/13 Javascript
Django框架多表查询实例分析
2018/07/04 Python
python实现一个简单的ping工具方法
2019/01/31 Python
使用tensorflow DataSet实现高效加载变长文本输入
2020/01/20 Python
Guess荷兰官网:美国服饰品牌
2020/01/22 全球购物
《画家乡》教学反思
2014/04/22 职场文书
2014县政府领导班子三严三实对照检查材料思想汇报
2014/09/26 职场文书
2015幼儿园庆元旦活动方案
2014/12/09 职场文书
小学生优秀评语
2014/12/29 职场文书
综合管理员岗位职责
2015/02/11 职场文书
自考生自我评价
2019/06/21 职场文书
当你焦虑迷茫时,请读读这6句话
2019/07/24 职场文书
vue封装数字翻牌器
2022/04/20 Vue.js
vue3不同环境下实现配置代理
2022/05/25 Vue.js