编写高质量Python (第29条) 用赋值表达式消除推导中的重复代码

第 29 条 用赋值表达式消除推导中的重复代码

​ 推导 list、dict 与 set 等变体结构时,经常要在多个地方用到同一个计算结果。例如,我们要给制作紧固件等公司编写程序来管理订单。顾客下单后,我们要判断当前的库存能否满足这份订单,也就是说,要核查每种产品的数量有没有达到可以发货的最低限制(8 个为一批,至少要有一批,才能发货)。

stocks = {'nails': 125,'screw': 35,'wingnuts': 8,'washers': 24,
}order = ['screws', 'wingnuts', 'clips']def get_batches(count, size):return count // sizeresult = {}
for name in order:count = stocks.get(name, 0)batches = get_batches(count, 8)if batches:result[name] = batchesprint(result)       >>>
{'screws': 4, 'wingnuts': 1}

​ 这段逻辑,如果改用字典来写,会简单一些(参见第 27 条)。

found = {name: get_batches(stocks.get(name, 0), 8)for name in orderif get_batches(stocks.get(name, 0), 8)}
print(found)>>>
{'screws': 4, 'wingnuts': 1}

​ 这样写虽然比刚才简短,但问题是,它把 get_batches(stock.get(name, 0), 8)写成了两遍。这样写会让代码看着比较乱,而且实际上,程序也没有必要把这个运算结果计算两遍。另外,如果这两个地方都忘了同步更新,那么程序就会出现 bug。例如,我们决定每一批不是 8 个,而是 4 个,那么需要把 get_batches 的第二个参数从 8 改成 4,但是,万一我们忘了同步修改另一个地方,那么代码就会出现问题(它会把大于等于 4 但是小于 8 的情况给漏掉)。

has_bug = {name: get_batches(stocks.get(name, 0), 4)for name in orderif get_batches(stocks.get(name, 0), 8)}print('Expected:', found)
print('Found:', has_bug)>>>
Expected: {'screws': 4, 'wingnuts': 1}
Found: {'screws': 8, 'wingnuts': 2}

​ 有个简单的办法可以解决这个问题,那就是在推导的过程中使用 Python 3.8 新引入的 := 操作符进行赋值表达(参见 第10条)。

found = {name: batches for name in orderif (batches := get_batches(stocks.get(name, 0), 8))}

​ 这条 batches := get_batches(…) 赋值表达式,能够从 stocks 字典中查到对应商品一共有几批,并且把这个批数放在 batches 变量里。这样的话,我们推导这个产品所对应批数时,就不用通过 get_batches 计算了,因为这样结果已经保存到 batches 里面了。这种方法只需要把 get 与 get_batches 调用一次即可,这样能够提高效率,因为我们不需要针对 order 列表中的每件产品都多做一次 get 与 get_batches。

​ 在推导过程中,描述新值的那一部分也可以出现赋值表达式。但如果在其他部分引用了定义在那一部分的变量,那么程序就可能在运行时出错。例如,如果写成了下面这样,那么程序就需要先考虑 for name, count in stocks.items() if tenth > 0,而这个时候,其中的 teeth 时没有得到定义。

result = {name: (tenth := count // 10)for name, count in stocks.items() if tenth > 0}>>>
Traceback ...
NameError: name 'tenth' is not defined

​ 如果推导逻辑不含条件,可以把赋值表达式移动到 if 条件里面,然后在描述新值的这一部分引用已经定义过的 tenth 变量。

result = {name: tenth for name, count in stocks.items() if (tenth := count // 10) > 0}>>>
{'nails': 12, 'screws': 3, 'washers': 2}

​ 如果推导逻辑不带条件,而表示新值的那一部分又使用了 := 操作符,那么操作符左边的变量就会泄漏到包含这条推导语句的那个作用域里面。(参见 第21条)。

half = [(last := count // 2) for count in stocks.values()]
print(f'Last item of {half} is {last}')>>>
Last item of [62, 17, 4, 12] is 12

​ 这与普通的 for 循环所用的那个循环变量相似。

for count in stocks.values():  # Leaks loop variablepass
print(f'Last item of {list(stocks.values())} is {count}')

​ 然而,推导语句中的 for 循环所使用的循环变量,是不会像刚才那样泄漏到外面的。

half = [count // 2 for count in stocks.values()]
print(half)
print(count)>>>
[62, 17, 4, 12]
Traceback ...
NameError: name 'count' is not defined. Did you mean: 'round'?

​ 最好不要泄漏循环变量,所以,建议赋值语句只出现在推导逻辑的条件之中。

​ 赋值表达式不仅可以用在推导过程中,而且可以用来编写生成器表达式( generator expression,参见 第 32 条)。下面这种写法创建的是迭代器,而不是字典实例,该迭代器会给出一对数值,其中第一个元素为产品的名字,第二个元素为这种产品的库存。

found = ((name, batches) for name in orderif (batches := get_batches(stocks.get(name, 0), 8)))
print(next(found))
print(next(found))>>>
('screws', 4)
('wingnuts', 1)

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

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

相关文章

C++ vector基本操作

目录 一、介绍 二、定义 三、迭代器 四、容量操作 1、size 2、capacity 3、empty 4、resize 5、reserve 总结(扩容机制) 五、增删查改 1、push_back & pop_back 2、find 3、insert 4、erase 5、swap 6、operator[] 一、介绍 vector…

前端CSS(层叠样式表)总结

CSS2总结 一、CSS基础 1. CSS简介 CSS 的全称为:层叠样式表 ( Cascading Style Sheets ) 。CSS 也是一种标记语言,用于给 HTML 结构设置样式,例如:文字大小、颜色、元素宽高等等。 简单理解: CSS 可以美化…

使用python脚本轻松实现ssh免密登陆配置

1.安装python和pip包 yum install -y python python-pip 2.pip安装依赖库 pip install pexpect # 此库用相当于linux中的expect命令 3.完整脚本 # codingUTF-8 import sys,os,pexpect,subprocesshost_controller"192.168.174.150" …

<软考>软件设计师-3程序设计语言基础(总结)

(一) 程序设计语言概述 1 程序设计语言的基本概念 1-1 程序设计语言的目的 程序设计语言是为了书写计算机程序而人为设计的符号语言,用于对计算过程进行描述、组织和推导。 1-2 程序语言分类 低级语言 : 机器语言(计算机硬件只能识别0和1的指令序列)&…

cmd 或者bat 创建快捷方式,并配置开机自动启动

echo off echo Set oWS WScript.CreateObject("WScript.Shell") > CreateShortcut.vbs echo sLinkFile "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\你的程序名称.lnk" >> CreateShortcut.vbs echo Set oLink oWS.CreateShortc…

【开放集检测OSR】RPL(Reciprocal Point Learning)、ARPL和ARPL+CS 概念辨析

一、RPL(Reciprocal Point Learning) 在开放集检测中,RPL(Reciprocal Point Learning)方法旨在识别出不属于任何已知类别的开放集样本。 RPL方法基于以下原则:开放集样本与已知类别之间的差异性较大&…

java设计模式学习之【装饰器模式】

文章目录 引言装饰器模式简介定义与用途实现方式 使用场景优势与劣势装饰器模式在Spring中的应用画图示例代码地址 引言 在日常生活中,我们常常对基本事物添加额外的装饰以增强其功能或美观。例如,给手机加一个保护壳来提升其防护能力,或者在…

SwiftUI 中创建一个自定义文件管理器只需4步!你敢信!?

概览 在 SwiftUI 中写一个自定义文件内容的管理器有多难呢? 答案可能超乎小伙伴们的想象:仅需4步!可谓是超级简单! 在本篇博文中,您将学到如下内容: 概览1. 第一步:定义文件类型2. 第二步&…

Dockerfile 指令的最佳实践

这些建议旨在帮助您创建一个高效且可维护的Dockerfile。 一、FROM 尽可能使用当前的官方镜像作为镜像的基础。Docker推荐Alpine镜像,因为它受到严格控制,体积小(目前不到6 MB),同时仍然是一个完整的Linux发行版。 FR…

从主从复制到哨兵模式(含Redis.config配置模板)

文章目录 前言一、主从复制1.概述2.作用3.模拟实践搭建场景模拟实践 二、哨兵模式1.概述2.配置使用3.优缺点4.sentinel.conf完整配置 总结 前言 从主从复制到哨兵模式。 一、主从复制 1.概述 主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Red…

0010Java安卓程序设计-ssm基于安卓的掌上校园系统

文章目录 **摘要**目录系统实现5.2管理员功能模块开发环境 编程技术交流、源码分享、模板分享、网课分享 企鹅🐧裙:776871563 摘要 随着Internet的发展,人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化,…

【Wrapper上下文包装器模式】

1.不管是什么类型的Redis,我们都可以包装为:GameRedis 2.对属性计算的需求,我们可以包装处MTHero, 然后整理出所有的属性,从而计算出升级战力变化等。

Livox_ros_driver2 消息 (msg) 类型对 SLAM 应用程序的适配

Title: Livox_ros_driver2 消息 (msg) 类型对 SLAM 应用程序的适配 文章目录 I 前言II. 查看 ROS 消息III. Livox ROS 驱动的消息类型IV. 适配修改应用程序V. 总结 I 前言 有时候, 拿到最新的 Livox 激光传感器, 比如 HAP, 原厂也提供了 ROS 驱动支持 livox_ros_drivers2 (htt…

UE蓝图 里的函数,编辑模式在Sequence里执行

在蓝图里创建CustomFunction ,把蓝图拖入Sequence ,添加事件 即可调用 如果需要在Sequence里K 蓝图里的变量,需要勾上向过场动画公开

JS中Map对象与object的区别

若想了解Map对象可以阅读本人这篇ES6初步了解Map Map对象与object有什么区别?让我为大家介绍一下吧! 共同点 二者都是以key-value的形式对数据进行存储 const obj {name:"zs",age:18}console.log(obj)let m new Map()m.set("name&quo…

ubuntu18.4安装拼音输入法

apt-get install ibus ibus-pinyin

《深入理解计算机系统》学习笔记 - 第三课 - 浮点数

Floating Point 浮点数 文章目录 Floating Point 浮点数分数二进制示例能代表的数浮点数的表示方式浮点数编码规格化值规格化值编码示例 非规格化的值特殊值 示例IEEE 编码的一些特殊属性四舍五入,相加,相乘四舍五入四舍五入的模式二进制数的四舍五入 浮…

带负离子的高速吹风筒方案介绍---【其利天下技术】

负离子吹风筒的产品概念,在吹风筒的产品系列里早就存在的。近年来,随着高速风筒的逐渐普及,产品商都开始把这些产品概念带了进来。一方面提升产品的核心竞争力,另一方面也是为了提升产品体验度,给用户带来不一样的产品…

夯实c基础

夯实c基础 区别: 图一的交换,(交换的是地址而不是两数)无法实现两数的交换。 题干以下程序的输出结果为( c  )。 void fun(int a, int b, int c){ ca*b; } void main( ){ int…

ssh安装和Gitee(码云)源码拉取

文章目录 安装ssh服务注册码云公钥设置码云账户SSH公钥安装git客户端和git-lfs源码获取 安装ssh服务 更新软件源: sudo apt-get update安装ssh服务 sudo apt-get install openssh-server检查ssh是否安装成功 which ssh输出: /usr/bin/ssh启动ssh 服…