当前位置: 首页 > news >正文

Python生成器 (Generators in Python)

Generators in Python

文章目录

  • Generators in Python
    • Introduction 导言
    • 贯穿全文的几句话
    • 为什么 Python 有生成器Generator?
    • 如何获得生成器Generator?
      • 1. 生成器表达式 Generator Expression
      • 2. 使用yield定义生成器Generator
    • 更多Generator应用实例
      • 表示无限的数据流infinite stream of data
      • 将多个生成器generators组成管道pipeline
    • Conclusion 结论

Introduction 导言

生成器generator是 Python 中用来生成迭代器Iterators的一个方便而强大的工具。本篇文章将通过一些示例来解释和深入介绍 Python 中的生成器generators。

如果您还没有完全理解 Itreators,不用担心,请阅读此篇文章。

贯穿全文的几句话

  • 只要一个函数function中使用了 yield 这个关键字,就代表这个函数function每次调用时返回的是一个生成器对象 generator object。这个生成器对象的类型是<class ‘generator’>。

  • 包含 yield 语句的函数function本身并不是生成器generator,它仍然是一个函数function。生成器generator是一个类class,而不是函数function。

  • 生成器generator是迭代器Iterator的一个子类subclass。

  • 生成器generator保存的是产生item的生成方法/算法,而不是items。

  • next() 函数只能用于生成器generator类型。不能用于函数function。

def func():yield "Hello"print(func)  # <function func at 0x10d55c0d0>
print(type(func))  # <class 'function'>g1 = func()
g2 = func()
print(id(g1), id(g2))  # 4519738272 4519739168
print(g1)  # <generator object func at 0x10d65bba0>
print(type(g1))  # <class 'generator'>
print(next(g1))  # Hello

为什么 Python 有生成器Generator?

我们可以通过在 Python 类class中实现implementing __iter__()__next__() 特殊方法special methods来获得迭代器Iterator。不过,这种方法有点复杂,尽管它有助于理解迭代器Iterators的真正工作原理。

通过生成器generators创建迭代器Iterators是一种更好、更方便的方法。事实上,生成器就是迭代器的子类the Generator is a subclass of the Iterator。

Iterable可迭代对象、Iterator迭代器 和 Generator生成器 的关系如下:

在这里插入图片描述

如上图所示,Iterator 是 Iterable 的子类,Generator 是 Iterator 的子类。

# 源码在_collections_abc.py
class Iterable(metaclass=ABCMeta):@abstractmethoddef __iter__(self): ...
# 源码在_collections_abc.py
class Iterator(Iterable):@abstractmethoddef __next__(self): raise StopIterationdef __iter__(self): return self
# 源码在_collections_abc.py
class Generator(Iterator):def __iter__(self):return selfdef __next__(self):"""Return the next item from the generator.When exhausted, raise StopIteration."""return self.send(None)@abstractmethoddef send(self, value):"""Send a value into the generator.Return next yielded value or raise StopIteration."""raise StopIteration@abstractmethoddef throw(self, typ, val=None, tb=None):"""Raise an exception in the generator.Return next yielded value or raise StopIteration."""...def close(self):"""Raise GeneratorExit inside generator."""...

生成器(Generator)与迭代器(Iterator)具有相同的作用,用于保存一个知道如何生成所需元素的方法method。在Python中操作一个大的列表是非常耗时的。如果我们每次只需要获取一个元素element,那么生成器generator就是一个很好的选择,它可以减少时间和空间成本。

在 Python 中,只要一个函数function中使用了 yield 这个关键字,就代表这个函数function每次调用时都是返回一个生成器对象 generator object,注意:包含 yield 语句的函数function本身并不是生成器generator,它仍然是一个函数function。生成器generator是一个类class,而不是函数function。而 yield 的作用就相当于让 Python 帮我们把一个“串行”的逻辑转换成 iterator 的形式。

生成器generator都是Iterator迭代器对象。

如何获得生成器Generator?

1. 生成器表达式 Generator Expression

生成器表达式generator expression是获取生成器generator的最简单方法。它与 列表推导式list comprehensions 非常相似。我们只需将括号brackets改为小括号parentheses。

my_list = [i for i in range(8)]
my_generator = (i for i in range(8))print(my_list)
print(my_generator)# [0, 1, 2, 3, 4, 5, 6, 7]
# <generator object <genexpr> at 0x7f8fc3ec9a40>

由于生成器generator保存的是item生成方法而不是items,因此我们需要使用 next() 函数逐个获取项目get items one by one,这与迭代器Iterator相同。当所有项目items都生成后, next() 函数将引发 StopIteration 错误信息。当然,我们也可以使用 for 循环来获取生成器generator中的项目items。

2. 使用yield定义生成器Generator

如果一个函数function包含 yield 语句,它就可以产生生成器generators。

def my_generator(maximum):n = 0while n < maximum:n += 1yield nreturn 'Done'g = my_generator(maximum=5)
print(g)  # <generator object my_generator at 0x10e269ba0>
print(next(g))  # 1
print(next(g))  # 2
print(next(g))  # 3
print(next(g))  # 4
print(next(g))  # 5
next(g)
# Traceback (most recent call last):
#   File "/usr/lib/python3.9/code.py", line 15, in <module>
#     next(g)
# StopIteration: Done

yield 表示 “产生”或“生成”produce。当程序执行到 yield 语句时,就会 "生产produce"一个值即项目item,而 next() 函数function就会在此暂停pauses there执行,等待下一次调用。

当我们再次使用 next() 函数function对生成器对象generator object进行调用,它会让生成器对象generator object从上一次暂停的位置继续执行,直到遇到下一个 yield 语句或者执行结束。

普通函数normal functions 与 包含 yield 的函数functions including yield 的主要区别在于执行流程execution flow

  • 普通函数按顺序执行executes sequentially,并在遇到 return 语句statement或到达最后一行final line时返回结果。
  • 包括 yield 的函数会在调用 next() 时执行,并在遇到 yield 语句时返回。再次调用 next() 时,将从上次暂停的 yield 语句处继续执行。

有一个例子:

def example():print('step 1')yield 1print('step 2')yield 2print('step 3')yield 3g = example()next(g)
# step 1
# 1
next(g)
# step 2
# 2
next(g)
# step 3
# 3
next(g)
# Traceback (most recent call last):
#   File "/usr/lib/python3.9/code.py", line 21, in <module>
#     next(g)
# StopIteration

注:包含 yield 语句的函数本身并不是生成器generator。它仍然是一个函数function,但每次调用这个函数function时都可以返回一个生成器对象return a generator,这个生成器对象的类型是<class ‘generator’>。生成器generator是一个类class,而不是函数function。(正如我们之前所说,生成器generator是迭代器Iterator的一个子类subclass)。

next() 只能用于生成器generator类型。不能用于函数function。

def my_generator(maximum):n = 0while n < maximum:yield nreturn 'Done'print(type(my_generator))  # <class 'function'>print(type(my_generator(5)))  # <class 'generator'>print(my_generator(5))  # <generator object my_generator at 0x10bc42ba0>print(next(my_generator(5)))  # 0print(next(my_generator))
# Traceback (most recent call last):
#   File "/usr/lib/python3.9/code.py", line 15, in <module>
#     print(next(my_generator))
# TypeError: 'function' object is not an iterator

更多Generator应用实例

到目前为止,我们知道生成器generators可以帮助我们保存生成项目items的算法,并在需要时生成项目items。与包含所有项目items的庞大列表list相比,生成器可以减少时间和内存成本。

表示无限的数据流infinite stream of data

事实上,生成器generator甚至可以表示无限的数据流infinite stream of data。例如:

def fibonacci():x, y = 0, 1while True:x, y = y, x + yyield xfib = fibonacci()
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
# ...

fib 是一个无限生成器infinite generator,我们可以根据自己的需要使用它。

将多个生成器generators组成管道pipeline

生成器generators的另一个有趣应用interesting application是,我们可以将一系列生成器generators组合起来,得到一个新的生成器generator,这在技术technically上被称为 “管道pipeline”。

def times_two(nums):for n in nums:yield n * 2def natural_number(maximum):x = 0while x < maximum:yield xx += 1p = times_two(natural_number(10))
print(type(p))  # <class 'generator'>
print(next(p))  # 0
print(next(p))  # 2
print(next(p))  # 4
print(next(p))  # 6
print(next(p))  # 8
print(next(p))  # 10
print(next(p))  # 12
# ...

如上例所示,我们可以使用现有的两个生成器generators来定义一个新的生成器generator。这不是很好吗?

Conclusion 结论

生成器Generator是 Python 中一种非常有用的机制useful mechanism,可以减少时间reduce time和内存开销memory costs。它保存的是产生项item的算法algorithm而不是项items。我们还可以使用生成器generators生成produce无限的数据流infinite data stream和管道pipelines。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【二叉树】【DFS】104.二叉树的最大深度
  • golang 图片加水印,字体文件从哪里找
  • 英语打卡分析12
  • LabVIEW与PID在温度测控系统中的应用
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • LTPI协议的理解——2、LTPI实现的底层架构
  • JS学习之-01
  • Visual Studio 配置DLL
  • 【Java 进阶篇】Redis 缓存优化:提升应用性能的不二选择
  • Android笔记(二十三):Paging3分页加载库结合Compose的实现分层数据源访问
  • 2024年汉字小达人区级样题预测(基于近年真题)和备考建议
  • 如何使用Docker将.Net6项目部署到Linux服务器(三)
  • o2o生活通全开源尊享版+多城市切换+企业付款+交友IM+平台快报
  • C# json 转匿名对象及C#关键字的处理
  • 毫米波雷达:从 3D 走向 4D
  • Java的Interrupt与线程中断
  • JS字符串转数字方法总结
  • nodejs调试方法
  • node和express搭建代理服务器(源码)
  • Theano - 导数
  • Vue 重置组件到初始状态
  • 第十八天-企业应用架构模式-基本模式
  • 搞机器学习要哪些技能
  • 构造函数(constructor)与原型链(prototype)关系
  • 基于axios的vue插件,让http请求更简单
  • 开源地图数据可视化库——mapnik
  • 批量截取pdf文件
  • 如何用vue打造一个移动端音乐播放器
  • 首页查询功能的一次实现过程
  • 跳前端坑前,先看看这个!!
  • 小程序 setData 学问多
  • 正则学习笔记
  • ​flutter 代码混淆
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • ​浅谈 Linux 中的 core dump 分析方法
  • #define用法
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • (day 12)JavaScript学习笔记(数组3)
  • (floyd+补集) poj 3275
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (论文阅读11/100)Fast R-CNN
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (论文阅读40-45)图像描述1
  • (篇九)MySQL常用内置函数
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (一)WLAN定义和基本架构转
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)可以带来幸福的一本书
  • (转)项目管理杂谈-我所期望的新人
  • .net 7和core版 SignalR