Skip to content

Latest commit

 

History

History
259 lines (182 loc) · 6.2 KB

20-运算符.md

File metadata and controls

259 lines (182 loc) · 6.2 KB

运算符魔法方法通常用于实现对象的运算、赋值等操作。

前言中介绍的 __add__ 就属于运算符魔法方法。让我们继续探讨。

比较运算符

使用 Python 魔法方法的一个巨大的好处,就是可以构建与内置类型相似行为的对象。

什么意思?比方说在某些语言中,要验证两个自定义对象是否相等,代码可能是这样:

if obj.isEqual(other):
    # ...

Python 中同样可以这样实现,但缺点是它是非标准的,你可能需要稍微花一点时间才知道它要做什么。另外,不同的程序员可能对函数的名称理解不同,从而使得同一个功能具有 equals()isEqual()equalTo() 等不同的名称。

让我们看看如果运用了魔法方法:

if obj == other:
    # ...

是不是一目了然?

要实现 == 运算符,只需要你实现 __eq__ 方法:

class Foo:
    def __init__(self, id):
        self.id = id
        
    def __eq__(self, other):
        return self.id == other.id


f1 = Foo(1)
f2 = Foo(1)
f3 = Foo(2)

print(f1 == f2, f2 == f3)
# 输出:
# True False

这就是魔法的力量。

类似的比较运算符还有:

  • __lt__
  • __le__
  • __ne__
  • __gt__
  • __ge__

分别实现 <<=!=>>= 运算符。

算数运算符

算数运算符用于常见的数学运算,如 + - * / 加减乘除等。

比如 __add__ 魔法方法可以实现加法操作。

整数相加的计算就相当于:

>>> num = 1
>>> num + 2
3
>>> num.__add__(2)
3

你可以让一个自定义的类实现 __add__ 魔法方法,从而使它也可以进行加法计算。

比如定义一个矢量:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __add__(self, other):
        new_x = self.x + other.x
        new_y = self.y + other.y
        return Vector(new_x, new_y)

由于实现了 __add__ 方法,这个矢量类就可以非常自然的相加:

>>> v1 = Vector(1, 2)
>>> v2 = Vector(3, 4)

>>> v3 = v1 + v2

>>> v3.x
4
>>> v3.y
6

类似的,我们就可以实现 Vector 类的整套加减乘除运算了:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    # 加
    def __add__(self, other):
        new_x = self.x + other.x
        new_y = self.y + other.y
        return Vector(new_x, new_y)

    # 减
    def __sub__(self, other):
        new_x = self.x - other.x
        new_y = self.y - other.y
        return Vector(new_x, new_y)
    
    # 乘
    def __mul__(self, other):
        new_x = self.x * other.x
        new_y = self.y * other.y
        return Vector(new_x, new_y)

    # 除
    def __truediv__(self, other):
        new_x = self.x / other.x
        new_y = self.y / other.y
        return Vector(new_x, new_y)

类似的算数运算符还有:

  • __matmul__
  • __floordiv__
  • __mod__
  • __pow__
  • __lshift__
  • __rshift__
  • __and__
  • __xor__
  • __or__

以上魔法方法分别实现 @//%**<<>>&^| 操作。

了解这些方法的功能即可。需要用到时稍加搜索,就能知道其需要的参数。

除此之外还有个 __divmod__ 方法,它把除数和余数运算结果结合起来,返回一个包含商和余数的元组,也就是把 __floordiv____mod__ 的作用融合在一起了。

反算数运算符

上面探讨的算数运算符要求位于运算符前面的对象实现,比如:

first + second

这个式子中,要求 first 必须实现 __add__ 方法。

但是如果你没办法保证 first 的具体实现,那么也可以在 second 实现反算数运算符 __radd__,达到同样的效果。

应用场景举例:second 是库提供的,而 first 是用户自行编写,没办法保证其能够实现 __add__

反算数运算的名称就是在正常算数运算符前面加字母 r,比如 __radd____rsub__,就不展开讲了。

增量赋值符

与反算数运算符类似的还有增量赋值符,比如最常用的 +=

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __iadd__(self, other):
        return Vector(self.x + other, self.y + other)


v1 = Vector(-1, 2)
v1 += 1
print(v1.x, v1.y)
# 输出:
# 0 3

其他的增量赋值符的还有 __isub____imul__ 等,也不展开列举了。

一元运算符

一元运算符只有一个操作符。

还是拿上面的 Vector 类举例,比方说我要实现取负的操作:

class Vector:
    def __neg__(self):
        return Vector(-self.x, -self.y)
    
    # 其他方法
    # ...

那么就可以这样取负了:

>>> v1 = Vector(-1, 2)
>>> v2 = -v1

>>> print(v2.x, v2.y)
1 -2

类似的一元运算符还有:

  • __pos__:取正,比如 +v1
  • __abs__:取绝对值,如 abs(v1)
  • __invert__:实现取反操作符 ~,二进制逐位取反。
  • __complex__:实现内建函数 complex(),取复数。
  • __int__:实现内建函数 int(),整型转换。
  • __float__:实现内建函数 float(),浮点数转换。
  • __round__:实现内建函数 round(),四舍五入。
  • __ceil__:实现内建函数 math.ceil(),大于原始值的最小整数。
  • __floor__:实现内建函数 math.floor(),小于原始值的最大整数
  • __trunc__:实现内建函数 math.trunc(),朝零取整
  • __index__:作为列表索引的数字。

稍微难理解的只有最后这个 __index__,用一个例子说明:

class Index:
    def __index__(self):
        return 1

my_list = [0, 1, 2]    
index = Index()

print(my_list[index])
# 输出:
# 1

总结

以上就是常见运算符魔法方法的用法了。

可能你觉得实现个加减乘除没多大用处。别急,这只是魔法方法中非常小的一部分。让我们下一章继续深入。


本系列文章开源发布于 Github,传送门:Python魔法方法漫游指南

看完文章想吐槽?欢迎留言告诉我!