Python面向对象编程第16篇 运算符重载

Posted 不剪发的Tony老师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python面向对象编程第16篇 运算符重载相关的知识,希望对你有一定的参考价值。

本篇我们学习 Python 运算符重载,了解如何使用内置运算符操作自定义的类对象。

运算符重载

以下示例创建了一个表示二维空间点的类,包含 x 坐标 和 y 坐标两个属性:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'(self.x,self.y)'

为了实现两个 Point2D 对象相加,我们可以定义一个 add() 方法:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'(self.x,self.y)'

    def add(self, point):
        if not isinstance(point, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x + point.x, self.y + point.y)

如果 point 参数不是 Point2D 类的实例,add() 方法将会返回一个错误;否则,它会返回一个新的 Point2D 对象,该对象的 x 和 y 坐标等于两个点的 x 和 y 坐标之和。

以下代码创建了两个 Point2D 类实例并使用 add() 方法将两个坐标点进行相加:

a = Point2D(10, 20)
b = Point2D(15, 25)
c = a.add(b)

print(c)

输出结果如下:

(25,45)

以上代码可以正常运行,但是 Python 提供了更好的实现方法。除了使用 add() 方法之外,我们还可以使用内置的 + 运算符:

c = a + b

当我们使用 + 运算符操作 Point2D 对象时,Python 会调用对象的 __add__() 方法。以下调用方法效果相同:

c = a + b
c = a.__add__(b)

上面的 __add__() 方法必须返回一个新的 Point2D 对象实例。

使用内置运算符操作自定义类型的功能被称为运算符重载。以下示例中的 Point2D 类实现了 __add__() 方法,可以支持 + 运算符:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'(self.x,self.y)'

    def __add__(self, point):
        if not isinstance(point, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x + point.x, self.y + point.y)


if __name__ == '__main__':
    a = Point2D(10, 20)
    b = Point2D(15, 25)
    c = a + b
    print(c)

输出结果如下:

(25,45)

运算符重载的特殊方法

下表列出了各种运算符以及对应的特殊方法:

运算符特殊方法
+__add__(self, other)
__sub__(self, other)
*__mul__(self, other)
/__truediv__(self, other)
//__floordiv__(self, other)
%__mod__(self, other)
**__pow__(self, other)
>>__rshift__(self, other)
<<__lshift__(self, other)
&__and__(self, other)
^__xor__(self, other)

例如,我们可以实现 Point2D 类的 __sub__() 方法,支持两个坐标点的减法运算:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'(self.x,self.y)'

    def __add__(self, point):
        if not isinstance(point, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x + point.x, self.y + point.y)

    def __sub__(self, other):
        if not isinstance(other, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x - other.x, self.y - other.y)


if __name__ == '__main__':
    a = Point2D(10, 20)
    b = Point2D(15, 25)
    c = b - a
    print(c)

inplace 运算符的重载

某些运算符还提供了 inplace 版本。例如,+ 运算符的 inplace 版本是 += 运算符。

对于不可变类型,例如元组、字符串以及数字,inplace 运算符执行计算操作但不会将结果赋予输入对象。对于可变类型,inplace 运算符直接对原始对象进行修改。

Python 同样为 inplace 运算符重载提供了许多特殊方法:

运算符特殊方法
+=__iadd__(self, other)
-=__isub__(self, other)
*=__imul__(self, other)
/=__itruediv__(self, other)
//=__ifloordiv__(self, other)
%=__imod__(self, other)
**=__ipow__(self, other)
>>=__irshift__(self, other)
<<=__ilshift__(self, other)
&=__iand__(self, other)
=
^=__ixor__(self, other)

我们来看一个重载 += 运算符的示例。

假设存在一个 cart 对象,我们想要将某个物品添加到购物车中。为此,我们可以为 Cart 类定义一个 add() 方法并调用该方法:

cart.add(item)

另外,我们也可以使用 Cart 类的 += 运算符,它可以为购物车添加一个物品:

cart += item

为了支持 += 运算符,我们需要实现 Cart 类的 __iadd__ 方法。

首先,定义一个 Item 类,它包含三个属性 name、quantity 以及 price。同时,它还拥有一个 amount 属性,返回物品的价格小计:

class Item:
    def __init__(self, name, qty, price):
        self.name = name
        self.qty = qty
        self.price = price

    @property
    def amount(self):
        return self.qty * self.price

    def __str__(self):
        return f'self.name self.qty $self.price $self.amount'

其次,定义 Cart 类并实现 __iadd__ 方法:

class Cart:
    def __init__(self):
        self.items = []

    def __iadd__(self, item):
        if not isinstance(item, Item):
            raise ValueError('The item must be an instance of Item')

        self.items.append(item)
        return self

    @property
    def total(self):
        return sum([item.amount for item in self.items])

    def __str__(self):
        if not self.items:
            return 'The cart is empty'

        return '\\n'.join([str(item) for item in self.items])

在 __iadd__ 方法中,如果 item 不是 Item 类的实例,将会抛出 ValueError 错误;否则,将 item 添加到物品列表属性中。

total 属性返回了所有物品的总价。

如果购物车中没有任何物品,__str__ 方法将会返回字符串“The cart is empty”;否则,它会返回一个包含所有物品的字符串。

然后使用 += 运算符将物品添加到购物车中:

if __name__ == '__main__':
    cart = Cart()

    cart += Item('Apple', 5, 2)
    cart += Item('Banana', 20, 1)
    cart += Item('Orange', 10, 1.5)

    print(cart)
    # print the total line
    print('-' * 30)
    print('Total: $', cart.total)

输出结果如下:

Apple   5       $2      $10
Banana  20      $1      $20
Orange  10      $1.5    $15.0
------------------------------
Total: $ 45.0

总结

  • 运算符重载使得自定义类可以使用内置的运算符。

以上是关于Python面向对象编程第16篇 运算符重载的主要内容,如果未能解决你的问题,请参考以下文章

Python入门-6面向对象编程:10特殊方法和运算符重载-特殊属性

Python面向对象的运算符重载

Python面向对象运算符重载

7Python全栈之路系列之面向对象运算符重载

Python面向对象之运算符重载

python 面向对象调用问题