python 重载一元运算符

本文讲述python如何实现重载运算符的,运算符重载的作用是让用户定义的对象使用中缀运算符(如 +|)或一元运算符(如 和 ~)。说得宽泛一些,在Python 中,函数调用(())、属性访问(.)和元素访问/ 切片 ([])也是运算符,不过这里只讨论一元运算符和中缀运算符。

在某些圈子中,运算符重载的名声并不好。这个语言特性可能(已经)被滥用,让程序员 困惑,导致缺陷和意料之外的性能瓶颈。但是,如果使用得当,API 会变得好用,代码会 变得易于阅读。Python 施加了一些限制,做好了灵活性、可用性和安全性方面的平衡:
1、不能重载内置类型的运算符
2、不能新建运算符,只能重载现有的
3、某些运算符不能重载——is、and、or 和 not(不过位运算符 &、| 和 ~ 可以)

一元运算符

-(__neg__)  # 一元取负算术运算符。如果 x 是 -2,那么 -x == 2。

+(__pos__) # 一元取正算术运算符。通常,x == +x,但也有一些例外。如果好奇,下面具体讲解。

~(__invert__) # 对整数按位取反,定义为 ~x == -(x+1)。如果 x 是 2,那么 ~x == -3。

__abs__  #  abs() 函数返回数字的绝对值。

支持一元运算符很简单,只需实现相应的特殊方法。这些特殊方法只有一个参数,self。 然后,使用符合所在类的逻辑实现。不过,要遵守运算符的一个基本规则:始终返回一个 新对象。也就是说,不能修改 self,要创建并返回合适类型的新实例。

对 - 和 + 来说,结果可能是与 self 同属一类的实例。多数时候,+ 最好返回 self 的副本。 abs(…) 的结果应该是一个标量。但是对 ~ 来说,很难说什么结果是合理的,因为可能不是处理整数的位,例如在 ORM 中,SQL WHERE 子句应该返回反集。

 def __abs__(self): 
 	return math.sqrt(sum(x * x for x in self)) 

def __neg__(self): 
	# 为了计算 -v,构建一个新 Vector 实例,把 self 的每个分量都取反。 
	return Vector(-x for x in self) 

def __pos__(self): 
	# 为了计算 +v,构建一个新 Vector 实例,传入 self 的各个分量。
	return Vector(self) 

下面来看看:x 和 +x 何时不相等

每个人都觉得 x == +x,而且在 Python 中,几乎所有情况下都是这样。但是,在标准库中有两例 x != +x 的情况。

1,第一例与 decimal.Decimal 类有关。如果 x 是 Decimal 实例,在算术运算的上下文中创 建,然后在不同的上下文中计算 +x,那么 x != +x。例如,x 所在的上下文使用某个精 度,而计算 +x 时,精度变了,如示例 :

"""
取+ 一元运算的俩个特例
"""
import decimal


if __name__ == '__main__':
    # 特例1:
    # 获取当前全局算术运算的上下文引用
    ctx = decimal.getcontext()
    # 把算术运算上下文的精度设为 40
    ctx.prec = 40
    # 使用当前精度计算 1/3
    one_third = decimal.Decimal('1') / decimal.Decimal('3')
    # 查看结果,小数点后有 40 个数字
    print(one_third)
    # one_third == +one_third 比较
    print(one_third == +one_third)
    # 把精度降低为 28,这是 Python 3.4 为 Decimal 算术运算设定的默认精度
    ctx.prec = 28
    # one_third == +one_third 比较
    print(one_third == +one_third)
    # 查看 +one_third,小数点后有 28 个数字。
    print(+one_third)

运行结果:
python 重载一元运算符_第1张图片
x != +x 的第二例在collections.Counter 的文档中,Counter 类实现了几个算术运算符,例如中缀 运算符 +,作用是把两个 Counter 实例的计数器加在一起。然而,从实用角度出发, Counter 相加时,负值和零值计数会从结果中剔除。而一元运算符 + 等同于加上一个 Counter,因此它产生一个新的 Counter 且仅保留大于零的计数器。

"""
取+ 一元运算的俩个特例
"""
import decimal
from collections import Counter


if __name__ == '__main__':
    # 特例1:
    # 获取当前全局算术运算的上下文引用
    # ctx = decimal.getcontext()
    # 把算术运算上下文的精度设为 40
    # ctx.prec = 40
    # 使用当前精度计算 1/3
    # one_third = decimal.Decimal('1') / decimal.Decimal('3')
    # 查看结果,小数点后有 40 个数字
    # print(one_third)
    # one_third == +one_third 比较
    # print(one_third == +one_third)
    # 把精度降低为 28,这是 Python 3.4 为 Decimal 算术运算设定的默认精度
    # ctx.prec = 28
    # one_third == +one_third 比较
    # print(one_third == +one_third)
    # 查看 +one_third,小数点后有 28 个数字。
    # print(+one_third)
    # 特例2
    ct = Counter('abracadabra')
    print(ct)
    ct['r'] = -3
    ct['d'] = 0
    print(ct)
    print(+ct)

运行结果:
python 重载一元运算符_第2张图片

你可能感兴趣的:(python,特殊方法)