返回介绍

13.2 一元运算符

发布于 2024-02-05 21:59:47 字数 3780 浏览 0 评论 0 收藏 0

在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文