- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- Python 术语表
- Python 版本表
- 排版约定
- 使用代码示例
- 第一部分 序幕
- 第 1 章 Python 数据模型
- 第二部分 数据结构
- 第 2 章 序列构成的数组
- 第 3 章 字典和集合
- 第 4 章 文本和字节序列
- 第三部分 把函数视作对象
- 第 5 章 一等函数
- 第 6 章 使用一等函数实现设计模式
- 第 7 章 函数装饰器和闭包
- 第四部分 面向对象惯用法
- 第 8 章 对象引用、可变性和垃圾回收
- 第 9 章 符合 Python 风格的对象
- 第 10 章 序列的修改、散列和切片
- 第 11 章 接口:从协议到抽象基类
- 第 12 章 继承的优缺点
- 第 13 章 正确重载运算符
- 第五部分 控制流程
- 第 14 章 可迭代的对象、迭代器和生成器
- 14.1 Sentence 类第1版:单词序列
- 14.2 可迭代的对象与迭代器的对比
- 14.3 Sentence 类第2版:典型的迭代器
- 14.4 Sentence 类第3版:生成器函数
- 14.5 Sentence 类第4版:惰性实现
- 14.6 Sentence 类第5版:生成器表达式
- 14.7 何时使用生成器表达式
- 14.8 另一个示例:等差数列生成器
- 14.9 标准库中的生成器函数
- 14.10 Python 3.3 中新出现的句法:yield from
- 14.11 可迭代的归约函数
- 14.12 深入分析 iter 函数
- 14.13 案例分析:在数据库转换工具中使用生成器
- 14.14 把生成器当成协程
- 14.15 本章小结
- 14.16 延伸阅读
- 第 15 章 上下文管理器和 else 块
- 第 16 章 协程
- 第 17 章 使用期物处理并发
- 第 18 章 使用 asyncio 包处理并发
- 第六部分 元编程
- 第 19 章 动态属性和特性
- 第 20 章 属性描述符
- 第 21 章 类元编程
- 结语
- 延伸阅读
- 附录 A 辅助脚本
- Python 术语表
- 作者简介
- 关于封面
13.2 一元运算符
在 Python 语言参考手册中,“6.5. Unary arithmetic and bitwise operations”一节 2列出了三个一元运算符。下面是这三个运算符和对应的特殊方法。
2现有版本是 6.6 节,而不是 6.5 节。——编者注
-(__neg__)
一元取负算术运算符。如果 x 是 -2,那么 -x == 2。
+(__pos__)
一元取正算术运算符。通常,x == +x,但也有一些例外。如果好奇,请阅读“x 和 +x 何时不相等”附注栏。
~(__invert__)
对整数按位取反,定义为 ~x == -(x+1)。如果 x 是 2,那么 ~x == -3。
Python 语言参考手册中的“Data Model”一章还把内置的 abs(...) 函数列为一元运算符。它对应的特殊方法是 __abs__,从 1.2.1 节起已经见过多次。
支持一元运算符很简单,只需实现相应的特殊方法。这些特殊方法只有一个参数,self。然后,使用符合所在类的逻辑实现。不过,要遵守运算符的一个基本规则:始终返回一个新对象。也就是说,不能修改 self,要创建并返回合适类型的新实例。
对 - 和 + 来说,结果可能是与 self 同属一类的实例。多数时候,+ 最好返回 self 的副本。abs(...) 的结果应该是一个标量。但是对 ~ 来说,很难说什么结果是合理的,因为可能不是处理整数的位,例如在 ORM 中,SQL WHERE 子句应该返回反集。
如前所述,我们将为第 10 章定义的 Vector 类实现几个新运算符。示例 13-1 列出了示例 10-16 实现的 __abs__ 方法,以及新增加的 __neg__ 和 __pos__ 一元运算符方法。
示例 13-1 vector_v6.py:把一元运算符 - 和 + 添加到示例 10-16 中
def __abs__(self): return math.sqrt(sum(x * x for x in self)) def __neg__(self): return Vector(-x for x in self) ➊ def __pos__(self): return Vector(self) ➋
❶ 为了计算 -v,构建一个新 Vector 实例,把 self 的每个分量都取反。
❷ 为了计算 +v,构建一个新 Vector 实例,传入 self 的各个分量。
还记得吗? Vector 实例是可迭代的对象,而且 Vector.__init__ 的参数是一个可迭代对象,因此 __neg__ 和 __pos__ 的实现短小精悍。
我们不打算实现 __invert__ 方法,因此如果用户在 Vector 实例上尝试计算 ~v,Python 会抛出 TypeError,而且输出明确的错误消息,“bad operand type for unary ~: 'Vector'”。
下述附注栏讨论一个奇怪的问题,能增长你的 + 一元运算符知识。接下来的重要话题是:重载向量加法运算符 +(见 13.3 节)。
x 和 +x 何时不相等
每个人都觉得 x == +x,而且在 Python 中,几乎所有情况下都是这样。但是,我在标准库中找到两例 x != +x 的情况。
第一例与 decimal.Decimal 类有关。如果 x 是 Decimal 实例,在算术运算的上下文中创建,然后在不同的上下文中计算 +x,那么 x != +x。例如,x 所在的上下文使用某个精度,而计算 +x 时,精度变了,如示例 13-2 所示。
示例 13-2 算术运算上下文的精度变化可能导致 x 不等于 +x
>>> import decimal >>> ctx = decimal.getcontext() ➊ >>> ctx.prec = 40 ➋ >>> one_third = decimal.Decimal('1') / decimal.Decimal('3') ➌ >>> one_third ➍ Decimal('0.3333333333333333333333333333333333333333') >>> one_third == +one_third ➎ True >>> ctx.prec = 28 ➏ >>> one_third == +one_third ➐ False >>> +one_third ➑ Decimal('0.3333333333333333333333333333')
❶ 获取当前全局算术运算的上下文引用。
❷ 把算术运算上下文的精度设为 40。
❸ 使用当前精度计算 1/3。
❹ 查看结果,小数点后有 40 个数字。
❺ one_third == +one_third 返回 True。
❻ 把精度降低为 28,这是 Python 3.4 为 Decimal 算术运算设定的默认精度。
❼ 现在,one_third == +one_third 返回 False。
❽ 查看 +one_third,小数点后有 28 个数字。
虽然每个 +one_third 表达式都会使用 one_third 的值创建一个新 Decimal 实例,但是会使用当前算术运算上下文的精度。
x != +x 的第二例在 collections.Counter 的文档中。Counter 类实现了几个算术运算符,例如中缀运算符 +,作用是把两个 Counter 实例的计数器加在一起。然而,从实用角度出发,Counter 相加时,负值和零值计数会从结果中剔除。而一元运算符 + 等同于加上一个空 Counter,因此它产生一个新的 Counter 且仅保留大于零的计数器。见示例 13-3。
示例 13-3 一元运算符 + 得到一个新 Counter 实例,但是没有零值和负值计数器3
>>> ct = Counter('abracadabra') >>> ct Counter({'a': 5, 'r': 2, 'b': 2, 'd': 1, 'c': 1}) >>> ct['r'] = -3 >>> ct['d'] = 0 >>> ct Counter({'a': 5, 'b': 2, 'c': 1, 'd': 0, 'r': -3}) >>> +ct Counter({'a': 5, 'b': 2, 'c': 1})
下面回归正题。
3应该在最前面加一行:>>> from collections import Counter。——编者注
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论