Python 正确重载运算符(增量赋值运算符)

增量赋值运算符

Vector 类已经支持增量赋值运算符 += 和 *= 了,如示例 13-15 所示。
示例 13-15 增量赋值不会修改不可变目标,而是新建实例,然后
重新绑定

>>> v1 = Vector([1, 2, 3])
>>> v1_alias = v1 # ➊
>>> id(v1) # ➋
4302860128
>>> v1 += Vector([4, 5, 6]) # ➌
>>> v1 # ➍
Vector([5.0, 7.0, 9.0])
>>> id(v1) # ➎
4302859904
>>> v1_alias # ➏
Vector([1.0, 2.0, 3.0])
>>> v1 *= 11 # ➐
>>> v1 # ➑
Vector([55.0, 77.0, 99.0])
>>> id(v1)
4302858336

❶ 复制一份,供后面审查 Vector([1, 2, 3]) 对象。
❷ 记住一开始绑定给 v1 的 Vector 实例的 ID。
❸ 增量加法运算。
❹ 结果与预期相符……
❺ ……但是创建了新的 Vector 实例。
❻ 审查 v1_alias,确认原来的 Vector 实例没被修改。
❼ 增量乘法运算。
❽ 同样,结果与预期相符,但是创建了新的 Vector 实例。

如果一个类没有实现表 13-1 列出的就地运算符,增量赋值运算符只是
语法糖:a += b 的作用与 a = a + b 完全一样。对不可变类型来说,
这是预期的行为,而且,如果定义了 __add__ 方法的话,不用编写额
外的代码,+= 就能使用。
然而,如果实现了就地运算符方法,例如 __iadd__,计算 a += b 的
结果时会调用就地运算符方法。这种运算符的名称表明,它们会就地修
改左操作数,而不会创建新对象作为结果。

不可变类型,如 Vector 类,一定不能实现就地特殊方法。
这是明显的事实,不过还是值得提出来。

为了展示如何实现就地运算符,我们将扩展示例 11-12 中的 BingoCage
类,实现 __add____iadd__ 方法。
我们把子类命名为 AddableBingoCage。示例 13-16 是我们想让 + 运算
符具有的行为。

示例 13-16 使用 + 运算符新建 AddableBingoCage 实例

>>> vowels = 'AEIOU'
>>> globe = AddableBingoCage(vowels)>>> globe.inspect()
('A', 'E', 'I', 'O', 'U')
>>> globe.pick() in vowels ➋
True
>>> len(globe.inspect())4 >>> globe2 =
AddableBingoCage('XYZ')>>> globe3 = globe + globe2
>>> len(globe3.inspect())7 >>> void =
globe +
[10, 20] ➏
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'AddableBingoCage' and 'list'

❶ 使用 5 个元素(vowels 中的各个字母)创建一个 globe 实例。
❷ 从中取出一个元素,确认它在 vowels 中。
❸ 确认 globe 的元素数量减少到 4 个了。
❹ 创建第二个实例,它有 3 个元素。
❺ 把前两个实例加在一起,创建第 3 个实例。这个实例有 7 个元素。
❻ AddableBingoCage 实例无法与列表相加,抛出 TypeError。那个
错误消息是 __add__ 方法返回 NotImplemented 时 Python 解释器输出
的。

AddableBingoCage 是可变的,实现 iadd 方法后的行为如示例
13-17 所示。

示例 13-17 可以使用 += 运算符载入现有的 AddableBingoCage
实例(接续示例 13-16)

>>> globe_orig = globe ➊
>>> len(globe.inspect())4 >>> globe += globe2 ➌
>>> len(globe.inspect())
7 >>> globe += ['M', '
N']>>> len(globe.inspect())
9 >>> globe is globe_orig ➎
True
>>> globe += 1 ➏
Traceback (most recent call last):
...
TypeError: right operand in += must be 'AddableBingoCage' or an iterable

❶ 复制一份,供后面检查对象的标识。
❷ 现在 globe 有 4 个元素。
❸ AddableBingoCage 实例可以从同属一类的其他实例那里接受元
素。
❹ += 的右操作数也可以是任何可迭代对象。
❺ 在这个示例中,globe 始终指代 globe_orig 对象。
❻ AddableBingoCage 实例不能与非可迭代对象相加,错误消息会指
明原因。

注意,与 + 相比,+= 运算符对第二个操作数更宽容。+ 运算符的两个操
作数必须是相同类型(这里是 AddableBingoCage),如若不然,结果
的类型可能让人摸不着头脑。而 += 的情况更明确,因为就地修改左操
作数,所以结果的类型是确定的。

通过观察内置 list 类型的工作方式,我确定了要对 + 和 +=
的行为做什么限制。 my_list + x 只能用于把两个列表加到一
起,而 my_list += x 可以使用右边可迭代对象 x 中的元素扩展左
边的列表。list.extend() 的行为也是这样的,它的参数可以是
任何可迭代对象。

我们明确了 AddableBingoCage 的行为,下面来看实现方式,如示例
13-18 所示。
示例 13-18 bingoaddable.py:AddableBingoCage 扩展
BingoCage,支持 + 和 +=

import itertools ➊
from tombola import Tombola
from bingo import BingoCage
class AddableBingoCage(BingoCage): ➋def __add__(self, other):if isinstance(other, Tombola): ➌return AddableBingoCage(self.inspect() + other.inspect()) ➍else:return NotImplementeddef __iadd__(self, other):if isinstance(other, Tombola):other_iterable = other.inspect() ➎else:try:other_iterable = iter(other)except TypeError: ➏self_cls = type(self).__name__msg = "right operand in += must be {!r} or an iterable"
raise TypeError(msg.format(self_cls))self.load(other_iterable) ➐return self ➑

❶ “PEP 8—Style Guide for Python
Code”(https://www.python.org/dev/peps/pep-0008/#imports)建议,把导
入标准库的语句放在导入自己编写的模块之前。
❷ AddableBingoCage 扩展 BingoCage。
add 方法的第二个操作数只能是 Tombola 实例。
❹ 如果 other 是 Tombola 实例,从中获取元素。
❺ 否则,尝试使用 other 创建迭代器。
❻ 如果尝试失败,抛出异常,并且告知用户该怎么做。如果可能,错
误消息应该明确指导用户怎么解决问题。
❼ 如果能执行到这里,把 other_iterable 载入 self。
❽ 重要提醒:增量赋值特殊方法必须返回 self。
通过示例 13-18 中 __add____iadd__ 返回结果的方式可以总结出就
地运算符的原理。

__add__

调用 AddableBingoCage 构造方法构建一个新实例,作为结果返
回。

iadd
把修改后的 self 作为结果返回。

最后,示例 13-18 中还有一点要注意:从设计上
看,AddableBingoCage 不用定义 __radd__ 方法,因为不需要。如果
右操作数是相同类型,那么正向方法 __add__ 会处理,因此,Python
计算 a + b 时,如果 a 是 AddableBingoCage 实例,而 b 不是,那么
会返回 NotImplemented,此时或许可以让 b 所属的类接手处理。可
是,如果表达式是 b + a,而 b 不是 AddableBingoCage 实例,返回
了 NotImplemented,那么 Python 最好放弃,抛出 TypeError,因为
无法处理 b。

一般来说,如果中缀运算符的正向方法(如 __mul__)只处
理与 self 属于同一类型的操作数,那就无需实现对应的反向方法
(如 __rmul__),因为按照定义,反向方法是为了处理类型不同
的操作数。

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

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

相关文章

XCUITest + Objective-C 详细示例

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】

redis分布式锁 Redisson在电商平台开发中的实际应用

目录 概述 Redis分布式锁的实现方式 1. 基于SETNX命令(String类型) 2. 使用SET命令的NX和EX参数(推荐方式) 3. 基于Lua脚本实现复杂逻辑 4. RedLock算法(多节点Redis实现) Redisson的分布式锁 Redis…

joomla 使用nginx服务器只能打开首页,其他页面404的解决方案

最近一个客户将Joomla4网站从原先的Apache服务器改为Nginx服务器,整个过程一切顺利,但还原网站后发现只能打开首页,其他页面都是404。这个问题需要修改nginx的配置文件来解决。 伪静态 在Apache中使用.htaccess来完成伪静态路由的转发&…

湖北理元理律师事务所企业债务纾困路径:司法重整中的再生之道

中小企业债务危机常呈现“担保链扩散”特征,单一债务可能引发企业崩盘。湖北理元理律师事务所通过预重整制度与企业债务重组技术,探索出“司法保护商业谈判”的纾困模式。 一、企业债务风险处置四步法 紧急止血 申请司法保护:通过诉前调解…

利用DeepWiki高效阅读项目源码

想获取更多高质量的Java技术文章?欢迎访问Java技术小馆官网,持续更新优质内容,助力技术成长 技术小馆官网 DeepWiki 是一个强大的工具,专为程序员提供开源项目源码的结构化文档和 AI 驱动的问答功能,帮助快速理解复杂…

django rest_framework 前端网页实现Token认证

rest_framework提供了几种认证方式:Session、Token等。Session是最简单的,几乎不用写任何代码就可以是实现,Token方式其实也不复杂,网上的教程一大把,但是最后都是用Postman这类工具来实现API调用的,通过这…

面试题-函数类型的重载是啥意思

在 TypeScript 中,函数重载(Function Overload) 是指为同一个函数提供多个不同的调用签名(参数类型和返回值类型的组合),但函数体只有一个实现。这样可以让函数在不同的输入下表现出不同的行为,…

磐基PaaS平台MongoDB组件SSPL许可证风险与合规性分析(上)

#作者:任少近 文章目录 1.背景与问题1.1.背景1.2.问题 3.SSPL条款解读分析3.1.条款0:定义条款3.2.条款一:源代码条款3.3.条款二:基本授权条款3.4.条款三:反规避保护条款3.5.条款四:逐字传播条款3.6.条款五…

「Linux文件及目录管理」输入输出重定向与管道

知识点解析 输入/输出重定向 标准输入(stdin):默认从键盘读取,文件描述符为0。标准输出(stdout):默认输出到终端,文件描述符为1。标准错误(stderr):默认输出到终端,文件描述符为2。重定向符号: >:覆盖输出到文件(如command > file)。>>:追加输出…

【Node】最佳Node.js后端开发模板推荐

Node.js 后端开发模板推荐 以下是几个优秀的Node.js后端模板,它们都适合二次开发,各自有不同的特点和适用场景: 1. Express基础模板 Express Generator (官方工具) 官方提供的快速搭建工具基础MVC结构简单易上手 npm install express-ge…

HALCON相机标定

相机标定简介: 首先,相机会产生畸变,即实际图像和拍摄图像不一致,可以是凸性也可以是凹性形变,相机标定的过程就是将畸变图像还原为原始图像,并将图像中的像素坐标转换为世界坐标。 形如:相机内…

Solidity 入门教程(二):值类型全解 —— 布尔、整数、地址与字节数组

在上一章中,我们写下了第一个 Solidity 合约并在 Remix 中成功运行。本章我们将深入了解 Solidity 中的几种常用值类型(Value Types),并通过示例代码在 Remix 进行验证。 一、Solidity 中的三种数据类型 在 Solidity 中&#xf…

16.大数据监控

0.说明 监控主要构成。 软件版本。 1.exporter监控配置 1.1 node_exporter 启动命令 nohup ./node_exporter &服务 创建文件 /etc/systemd/system/node_exporter.service: [Unit] DescriptionPrometheus Node Exporter Wantsnetwork-online.target Aft…

Tomcat项目本地部署(Servlet为例)

在Windows上部署 在idea中打开项目 首先我们需要准备一个Servlet项目,我之前的Servlet项目是用eclipse写的,这种情况下如果用idea直接打开的话会出现左侧目录无法显示的情况,这个时候我们就需要用别的方法打开 打开项目管理 如下图&#…

安装MySQL 5.7导入数据,修改密码,创建账号并授权

1. 准备工作 sudo yum update -y sudo yum install -y wget libaio numactl 2. 下载 MySQL 5.7 二进制包 wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz 3. 创建 MySQL 用户和组 sudo groupadd mysql sudo useradd -r -g m…

基础RAG实现,最佳入门选择(八)

RAG重排序 RAG重排序技术以提高RAG系统中的检索质量。重新排序充当初始检索后的第二个过滤步骤,以确保最相关的内容用于响应生成。 重排序的关键概念 1.初始检索:使用基本相似度搜索的第一遍(准确度较低但速度更快) 2.文档评分…

Spring Boot 常用注解整理

Spring & Spring Boot 常用注解整理 现代的 Spring 与 Spring Boot 应用大量使用注解来简化配置、管理组件和实现各种框架功能。本文系统整理了常用的 Spring/Spring Boot 注解,按照功能分类进行介绍。每个注解都会涵盖其含义、提供来源、应用场景以及代码示例…

深入理解 Cross-Entropy 损失函数:从原理到实践

在深度学习中,损失函数是衡量模型性能的关键指标之一。对于多分类问题,Cross-Entropy 损失函数 是最常用的选择之一。它不仅能够有效衡量模型输出与真实标签之间的差异,还能通过梯度下降法指导模型的优化。本文将深入探讨 Cross-Entropy 损失…

Vim-vimrc保存文件自动移除行末尾空格

Vim-vimrc保存文件自动移除行末尾空格 这段代码通过设置 autocmd 和自定义函数,确保每次保存文件时都自动删除文件中的行尾空格,同时不会影响光标和视图的位置。它适用于所有文件类型,并且删除操作不会引入错误,即使没有行尾空格的…

Occt几何内核快速入门

本文简单介绍 Open Cascade Technology(OCCT),提供了下载地址和文档地址。通过OCCT的测试工具Draw,展示了OCCT的一些功能特性。介绍了OCCT集成开发的演示代码,提供了源代码下载地址和编译过程文件。 一、简介 Open C…