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):
    @abstractmethod
    def __iter__(self): ...
# 源码在_collections_abc.py
class Iterator(Iterable):
    @abstractmethod
    def __next__(self): 
        raise StopIteration
    def __iter__(self): 
        return self
# 源码在_collections_abc.py
class Generator(Iterator):
    def __iter__(self):
        return self
    def __next__(self):
        """Return the next item from the generator.
        When exhausted, raise StopIteration.
        """
        return self.send(None)
    @abstractmethod
    def send(self, value):
        """Send a value into the generator.
        Return next yielded value or raise StopIteration.
        """
        raise StopIteration
    @abstractmethod
    def 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 = 0
    while n < maximum:
        n += 1
        yield n
    return '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 1
    print('step 2')
    yield 2
    print('step 3')
    yield 3


g = 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 = 0
    while n < maximum:
        yield n
    return '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)))  # 0

print(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, 1
    while True:
        x, y = y, x + y
        yield x

fib = 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 * 2

def natural_number(maximum):
    x = 0
    while x < maximum:
        yield x
        x += 1

p = 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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/279687.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

准备用vscode代替sourceinsight

vscode版本1.85.1 有的符号&#xff0c;sourceinsight解析不到。 看网上说vscode内置了ripgrep&#xff0c;但ctrlshiftf在文件里查找的时候&#xff0c;速度特别慢&#xff0c;根本不像ripgrep的速度。ripgrep的速度是很快的。 但今天再查询&#xff0c;速度又很快了&#x…

Large-Precision Sign using PBS

参考文献&#xff1a; [CLOT21] Chillotti I, Ligier D, Orfila J B, et al. Improved programmable bootstrapping with larger precision and efficient arithmetic circuits for TFHE[C]//Advances in Cryptology–ASIACRYPT 2021: 27th International Conference on the T…

前后端分离nodejs+vue+ElementUi网上订餐系统69b9

课题主要分为两大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括个人中心、用户管理、菜品类型管理、菜品信息管理、留言反馈、在线交流、系统管理、订单管理等&#xff1b; 运行软件:vscode 前端nodejsvueElementUi 语言 node.js 框架&#xff1a;Express/k…

10.定时器各功能分析及编码

知识汇总&#xff1a; STM32的定时器有三种&#xff0c;高级定时器&#xff0c;通用定时器&#xff0c;基本定时器 就是功能多与少的差别&#xff0c;下面来逐个解释功能&#xff1a;在此之前&#xff0c;需要对几个概念有认知 几个概念&#xff1a; 1.定时器时钟频率&…

【论文笔记】Radar Fields: An Extension of Radiance Fields to SAR

原文链接&#xff1a;https://arxiv.org/abs/2312.12961 1. 引言 本文针对合成孔径雷达&#xff08;SAR&#xff09;的3D重建&#xff0c;提出雷达场&#xff0c;基于多个SAR对场景的测量学习体积模型。 3. 辐射场的介绍 NeRF将静态场景表达为连续的体积函数 F \mathcal{F}…

长城杯2021政企组-魔鬼凯撒的RC4茶室 WP

魔鬼凯撒的RC4茶室 知识点&#xff1a;UPX 移位密码 XOR 分析 查壳 32bit&#xff1b;UPX壳&#xff0c;upx -d直接脱。 查看主函数。 第一处输入Str1然后做一个比较。这里进去。 有个小技巧&#xff0c;这里传入的参数是Str字符串&#xff0c;但是原本IDA自动识别出来的…

智能监控平台/视频共享融合系统EasyCVR海康设备国标GB28181接入流程

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

shiro1.10版本后-IniSecurityManagerFactory过期失效

1、问题概述&#xff1f; 今天在研究了shiro的新版本shiro1.13.0版本&#xff0c;发现用了很长时间的IniSecurityManagerFactory工厂失效了。 从下图中可以看出&#xff0c;在新版本中IniSecurityManagerFactory被打上了过期线了。 那么问题来了&#xff0c;新版本如何使用呢…

Python 命令补全工具 argcomplete

1. 概述 在使用Python 命令或者 Python的命令行工具的时候&#xff0c;一个痛点是没有补全。比如python -m后面输入包名字&#xff0c;就没有提示&#xff0c;每次想运行一个http server的时候&#xff0c;都需要搜索一下http服务的包名。另外&#xff0c;像pip&#xff0c;pi…

Java注解学习,一文掌握@Autowired 和 @Resource 注解区别

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

【前端面经】即时设计

目录 前言一面git 常见命令跨窗口通信vue 响应式原理发布订阅模式翻转二叉树Promise.all()扁平化数组面试官建议 二面Event Loop 原理Promise 相关css 描边方式requestAnimationReact 18 新特性JSX 相关react 输出两次函数式编程React 批处理机制http请求头有哪些本地存储性能优…

秒杀系统的设计思路(应对高并发,超卖等问题的解决思路)

首先我们先看一下设计秒杀系统时&#xff0c;我们应该考虑的问题。 解决方案&#xff1a; 一.页面静态化结合CDN内容分发 前端把能提前放入cdn服务器的东西都放进去&#xff0c;反正把所有能提升效率的步骤都做一下&#xff0c;减少真正秒杀时候服务器的压力。 秒杀活动的页面…

年度总结和规划

年度总结和规划 目录概述需求&#xff1a; 设计思路实现思路分析1.技术总结2.管理总结3.职业计划比较 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait…

利用F12和Fiddler抓包

网络基础 http 而http协议又分为下面的部分,点击具体条目后可以查看详细信息 http请求消息:请求行(请求方法),请求路径,请求头,请求体(载荷) http响应消息:响应行(响应状态码),响应头&#xff0c;响应体 请求行 即请求方法 get post put patch 响应行 即响应码,常见响应状态…

Java基础02-Java编程基础

文章目录 变量&#xff08;Variables&#xff09;局部变量和成员变量局部变量&#xff08;Local Variables&#xff09;成员变量&#xff08;Instance Variables&#xff09; 标识符&#xff08;Identifiers&#xff09;八种基本数据类型原始数据类型&#xff08;Primitive Dat…

java中如何使用elasticsearch—RestClient操作文档(CRUD)

目录 一、案例分析 二、Java代码中操作文档 2.1 初始化JavaRestClient 2.2 添加数据到索引库 2.3 根据id查询数据 2.4 根据id修改数据 2.4 删除操作 三、java代码对文档进行操作的基本步骤 一、案例分析 去数据库查询酒店数据&#xff0c;导入到hotel索引库&#xff0…

python+django超市进销存仓库管理系统s5264

本次设计任务是要设计一个超市进销存系统&#xff0c;通过这个系统能够满足超市进销存系统的管理及员工的超市进销存管理功能。系统的主要功能包括&#xff1a;首页、个人中心、员工管理、客户管理、供应商管理、承运商管理、仓库信息管理、商品类别管理、由管理员和员工&#…

Metapreter 详细教程--进阶教程

常用命令 基本命令 命令说明sysinfo查看系统信息ls列出目录或文件夹pwd获取当前目录地址cd切换目录&#xff0c;注意这里的win系统需要用用两个反斜杠来分割&#xff08;cd c:\windows\system32 &#xff09;help帮助getuid查看当前用户是谁getpid查看当前进程号ps查看所有进…

API 开放平台项目(已整理,已废弃)

项目大纲 前端 React 18Ant Design Pro 5.x 脚手架Ant Design & Procomponents 组件库Umi 4 前端框架OpenAPI 前端代码生成 后端 Java Spring BootMySQL 数据库MyBatis-Plus 及 MyBatis X 自动生成API 签名认证&#xff08;Http 调用&#xff09;Spring Boot Starter&#…

数据库——简单查询复杂查询

1.实验内容及原理 1. 在 Windows 系统中安装 VMWare 虚拟机&#xff0c;在 VMWare 中安装 Ubuntu 系统,并在 Ubuntu 中搭建 LAMP 实验环境。 2. 使用 MySQL 进行一些基本操作&#xff1a; &#xff08;1&#xff09;登录 MySQL&#xff0c;在 MySQL 中创建用户&#xff0c;…