编写高质量Python (第35条) 不要通过 throw 变换生成器状态

第 35 条 不要通过 throw 变换生成器状态

​ 除 yield from 表达式(参见 第 33 条) 与 send 方法(参见 第 34 条)外,生成器还有一个高级功能,就是可以把调用者通过 throw 方法传过来的 Exception 实例重新抛出。这个 throw 方法用起来很简单:如果调用这个方法,那么生成器下次推进时,就不会像平常那样,直接走到下一条 yield 表达式那里,而是通过 throw 方法传入的异常重新抛出。下面用代码演示这种效果。

class MyError(Exception):passdef my_generator():yield 1yield 2yield 3it = my_generator()
print(next(it))
print(next(it))
print(it.throw(MyError('test error')))>>>
__main__.MyError: test error
1
2

​ 生成器函数可以用标准的 try/expect 复合语句把 yield 表达式包裹起来,如果函数执行到了这条表达式这里,而这次即将继续执行时,又发现外界通过 throw 方法给自己注入了异常,那么这个异常就会被 try 结构捕获下来,如果捕获之后不继续抛出异常,那么生成器函数就会推进到下一条 yield 表达式(更多异常处理,参见 第 65 条)。

class MyError(Exception):passdef my_generator():yield 1try:yield 2except MyError:print("Get Error")else:yield 3yield 4it = my_generator()
print(next(it))  # Yield 1
print(next(it))  # Yield 2
print(it.throw(MyError('test error')))>>>
1
2
Get Error
4

​ 这项机制会在生成器与调用者形成双向通道(另一种双向通信通道, 参见第34条),这在某些情况下是有用的。例如,要编写一个可以重置的计时器程序。笔者定义下面的 Reset 异常与 Timer 生成器方法,让调用者可以在 timer 给出的迭代器上通过 throw 方法注入 Reset 异常,令计时器重置。

def Reset(exception):passdef timer(period):current = periodwhile current:current -= 1try:yield currentexcept Reset:current = period

​ 按照这种写法,如果 timer 正准备从 yield 表达式往下递推时,发现有人注入了 Reset 异常,那么它就会把这个异常捕获下来,并进入 except 分支,在这里它会把表示倒计时的 current 变量调整成最初的 period 值。

​ 这个计时器可以与外界某个按秒查询的输入机制对接起来。笔者定义一个函数以驱动 timer 生成器所给出的那个 it 迭代器,并根据外界的情况做处理,如果外界要求重置,那就通过 it 迭代器的 throw 方法给计时器注入 Reset 变量,如果外界没有这样要求,那就调用 annouce 函数打印所给的倒计时值。

def check_for_reset():#  Poll for external event...def announce(remaining):print(f'{remaining} ticks remaining')def run():it = timer(4)while True:try:if check_for_reset():current = it.throw(Reset())else:current = next(it)except StopIteration:breakelse:announce(current)run()>>>
3 ticks remaining
2 ticks remaining
1 ticks remaining
3 ticks remaining
2 ticks remaining
1 ticks remaining
3 ticks remaining
2 ticks remaining
1 ticks remaining
0 ticks remaining

​ 这样写用了很多嵌套结构,我们要判断 check_for_reset() 函数的返回值,以确定是应该通过 it.throw 注入 Reset 异常,还是应该通过 next 推进迭代器。如果要是推进迭代器,那么还得捕获 StopIteration 异常:若是捕获到了这种异常,那说明迭代器已经走到终点,则要执行 break 跳出 while 循环; 如果没有捕获到,则应该采用 annouce 函数打印倒计时。这会让代码很乱。

​ 有个简单的办法,能够改写这段代码,那就是利用可迭代的容器对象(参见 第 31 条)定义一个有状态的必包(参见 第 38 条)。下面的代码就写了这样一个 Timer 类,并通过它重新实现刚才的 timer 生成器。

class Timer:def __init__(self, period):self.current = periodself.period = perioddef reset(self):self.current = self.perioddef __iter__(self):while self.current:self.current -= 1yield self.current

​ 现在,run 函数就好写多了,因为它只需要用 for 循环 迭代这个 timer 即可。

def run():timer = Timer(4)for current in timer:if check_for_reset():timer.reset()announce(current)
run()>>>
3 ticks remaining
2 ticks remaining
1 ticks remaining
3 ticks remaining
2 ticks remaining
1 ticks remaining
3 ticks remaining
2 ticks remaining
1 ticks remaining
0 ticks remaining

​ 这样写所输出的结果与前面一样,但是这种实现方法理解起来更容易。凡是想用生成器与异常来实现的功能,通常都可以改用异步机制去做(参见 第 60 条)。如果确实遇到了这里讲到的这种需求,那么更应该通过可迭代的类实现生成器,而不要用 throw 方法注入异常。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue 3 中的 Teleport 特性详解

引言 在 Vue 3 中,引入了一个名为 Teleport 的新特性。这个特性允许开发者将组件的子组件“传送”到 DOM 中的任意位置,而不仅仅是它们的直接父级内部。这一功能在处理如模态框、弹出菜单、提示框等需要从其原始位置在视觉上移动到其他地方的用户界面元…

Spring Boot与Spring Boot MVC:构建现代化Web应用的利器

Spring Boot与Spring Boot MVC:构建现代化Web应用的利器 在当今的软件开发领域,特别是在Java生态系统中,Spring框架已经成为构建企业级应用程序的首选。而在Spring的众多子项目中,Spring Boot和Spring MVC是两个非常重要的组成部…

C++_数据类型_字符串型

作用 用于表示一串字符 两种风格 C风格字符串&#xff1a;char 变量名[] "字符串值” 示例 注意 C风格的字符串要用双括号括起来 C风格字符串&#xff1a;string 变量名 "字符串值” 注意 用C风格字符串的时候&#xff0c;要包含这个头文件#include <st…

PostgreSQL常用SQL语句

文章目录 PostgreSQL常用SQL语句免密交互增删改查备份恢复数据迁移用户管理权限管理进程管理查询优化PostgreSQL常用SQL语句 PostgreSQL部署,参见PostgreSQL部署与配置 免密交互 命令行执行SQL语句或备份、恢复时,有以下两种方式 1.交互式

【比较mybatis、lazy、sqltoy、lambda、操作数据 】操作批量新增、分页查询【一】

orm框架使用Lambda性能比较 环境&#xff1a; idea jdk17 spring boot 3.0.7 mysql 8.0测试条件常规对象 orm 框架是否支持xml是否支持 Lambda对比版本mybatis☑️☑️3.5.4sqltoy☑️☑️5.2.98lazy✖️☑️1.2.3-JDK17 数据库表(含有唯一性索引s_u) CREATE TABLE sys_u…

吴恩达机器学习-可选实验室-梯度下降-Gradient Descent for Linear Regression

文章目录 目标工具问题陈述计算损失梯度下降总结执行梯度下降梯度下降法成本与梯度下降的迭代预测绘制祝贺 目标 在本实验中&#xff0c;你将:使用梯度下降自动化优化w和b的过程 工具 在本实验中&#xff0c;我们将使用: NumPy&#xff0c;一个流行的科学计算库Matplotlib&…

【茶话数据结构】查找最短路径——Dijkstra算法详解(保姆式详细图解,步步紧逼,保你学会)

&#x1f4af; 博客内容&#xff1a;【茶话数据结构】查找最短路径——Dijkstra算法详解 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f989;所属专栏&#xff1a;数据结构笔记 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实…

【学习心得】为Django项目创建专用MySQL用户并赋予权限

一、问题描述 也许你在本地开发Django项目的时候不会关心&#xff0c;项目A所用的MySQL数据库能否被项目B访问。但若你使用的公司服务器or学校服务器&#xff0c;这种情况下很多人共用一个MySQL&#xff0c;你就会担心别人或别的项目胡乱访问你正在开发的项目所使用的数据库。这…

算法D33 | 贪心算法3 | 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

1005.K次取反后最大化的数组和 本题简单一些&#xff0c;估计大家不用想着贪心 &#xff0c;用自己直觉也会有思路。 代码随想录 Python: class Solution:def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:nums.sort(keylambda x: abs(x), reverseT…

【python】遵守 robots.txt 规则的数据爬虫程序

程序1 编写一个遵守 robots.txt 规则的数据爬虫程序涉及到多个步骤&#xff0c;包括请求网页、解析 robots.txt 文件、扫描网页内容、存储数据以及处理异常。由于编程语言众多&#xff0c;且每种语言编写爬虫程序的方式可能有所不同&#xff0c;以下将使用 Python 语言举例&am…

【论文】A Survey of Monte Carlo Tree Search Methods阅读笔记

本文主要是将有关蒙特卡洛树搜索的文献&#xff08;2011年之前&#xff09;进行归纳&#xff0c;概述了核心算法的推导&#xff0c;给出了已经提出的许多变化和改进的一些结构&#xff0c;并总结了MCTS方法已经应用于的博弈和其他领域的结果。 蒙特卡洛树搜索是一种通过在决策…

Redis在中国火爆,为何MongoDB更受欢迎国外?

一、概念 Redis Redis&#xff08;Remote Dictionary Server&#xff09;是一个使用ANSI C编写的开源、支持网络、基于内存、分布式、可选持久性的键值对存储数据库。Redis是由Salvatore Sanfilippo于2009年启动开发的&#xff0c;首个版本于同年5月发布。 MongoDB MongoDB…

C++练手题

第 1 题 【 问答题 】 • 红与黑 有一间长方形的房子&#xff0c; 地上铺了红色、 黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上&#xff0c; 只能向相邻的黑色瓷砖移动。 请写一个程序&#xff0c; 计算你总共能够到达多少块黑色的瓷砖。 时间限制&#xff1a; 1000…

基于R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析

在自然和社会科学领域有大量与地理或空间有关的数据&#xff0c;这一类数据一般具有严重的空间异质性&#xff0c;而通常的统计学方法并不能处理空间异质性&#xff0c;因而对此类型的数据无能为力。以地理加权回归为基础的一系列方法&#xff1a;经典地理加权回归&#xff0c;…

Linux相关小技巧《三》

需求&#xff1a; 前一段时间有收到这样的一个关于linux用户的权限相关的需求&#xff0c;在centos上给用户创建一个用SSH的密钥访问服务器&#xff0c;另给该用户添加到root权限组。记录下了步骤&#xff0c;分享给大家。 步骤&#xff1a; 添加root用户组&#xff1a; gr…

跳跃游戏问题(算法村第十七关黄金挑战)

跳跃游戏 55. 跳跃游戏 - 力扣&#xff08;LeetCode&#xff09; 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &…

人工智能-零基础

机缘 扩充下知识栈&#xff0c;准备零基础开始 人工智能零基础 日常 日常水一下博客… 憧憬 努力成为一个会人工智能的程序员

软考笔记--构件与软件复用

构件也称为组件&#xff08;component&#xff09;&#xff0c;是一个功能相对独立的具有可复用价值的软件单元。在面向对象的方法中&#xff0c;一个构件有一组对象组成&#xff0c;包含可一些协作的类的集成&#xff0c;它们协同工作来提供一种系统功能。可复用性是指系统和其…

138.乐理基础-等音、等音程的意义

上一个内容&#xff1a;137.乐理基础-协和音程、不协和音程 上一个内容里练习的答案&#xff1a; 等音、等音程的意义&#xff0c;首先在 19.音阶 里写了&#xff0c;一个调使用的音阶应当是从主音快开始&#xff0c;以阶梯状的形式进行到主音结束&#xff0c;这样才能明显从乐…

在docker中运行 pip 报错 Can‘t start new thread

原因源头 stackoverflowhis is because the default seccomp profile of Docker 20.10.9 is not adjusted to support the clone() syscall wrapper of glibc 2.34 adopted in Ubuntu 21.10 and Fedora 35.由于docker 版本与最新版 python 容器冲突导致 解决方案 以下三种方…