运算符重载 返回类型说明符后加&_Python3基础:从独特的角度看运算符重载

d06f2db8-d214-eb11-8da9-e4434bdf6706.png

本文并不是用来向你介绍运算符重载的基础知识,如果你想了解运算符重载的基础知识,本文可能并不适合你。本文的目的是从独特的角度介绍运算符重载,让你更深入地,更本质地了解什么是运算符重载。

0、什么是运算符重载

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

每个运算符都有自己特定的语法表达方式,例如加法运算符+要求其左右两边各有一个对象;括号运算符,要求其左边是一个可调用对象(函数对象),括号中需要一个任意长度的参数列表;等等。所谓运算符重载,说白了,就是让对象支持某个运算符的语法表达方式。

如果我们实现了一个类,这个类没有重载加法运算,那么这个类的对象就不支持加法运算表达式的语法。我们可以看一下如下的例子(如果你看不懂,你应该先了解一下运算符重载的知识再阅读本文):

a 

1、对象运算符表达式的本质

正如你在第0节的例子所看到的,运算符表达式实现上,是一个成员函数。换句话说,运算符表达式其实是一个语法糖,如果你重载了某个运算符(实现其对应的成员函数),你就可以使用这个运算符的特定表达式(语法糖)来调用重载的函数。

在Python里,万物皆对象。那怕一个整数常量10,它也是一个整数类型的对象。当我们对10进行加法运算的时候,事实上就是调用了10的加法运算函数。如下的例子就很好地证实了我们本节的结论:

# 定义两个整数,事实上a和b都是整形对象
a = 10
b = 8

# 由于整型重载了加法运算符,即重载了函数__add__
# 在运行阶段, a + b 会被转成a.__add__(b)的形式运行
# 所以如下的两行代码是等价的
c = a + b
c = a.__add__(b)

# 你甚至可以直接调用数值的__add__函数
# 以下两行代码是等价的
# 思考题:(10)的括号不可去掉,为什么?
c = (10).__add__(8)
c = 10 + 8

2、运算符重载需要注意些什么

在正常的加法运算中,表达式会返回加法运算的结果,而加号+两边的对象并不会被修改,我们称这个为加法运算的惯例。每个运行符都有自己的惯例。但同时,你也可以不遵循这些惯例。

我们先来看一个例子:

class A:
    def __init__(self, v):
        self.v = v 

    def __add__(self, v):
        self.v -= v

a = A(10)

# 以下的代码执行之后,可能并不会得到你想要的结果
# a.v的值为2,而c则为None
c = a + 8

在上面的例子中,a的值被修改了;运算符并没做加法运算,而是做了减法;同时,由于__add__并没有返回任何东西(没有返回东西,等同于返回None),所以c的值为None。我们看到, 这个例子就没有遵循加法运算符的惯例,但Python并没有阻止你这么做,正如第0节所介绍的,你可以在语法范围内对运算符进行重新定义,为所欲为。

虽然你可以这么做,但在实际开过程中,你应该尽量地遵循运算符的惯例。例如加法运算符最好是做“加法”逻辑的运算(如数值相加,字符串串连,集合合并等);运算符函数应该返回加法的结果;如果可能的话,尽量不要修改加号+左右两边的对象的值。

事实上,如果你要把加法运算的结果保存到a,即符号左边的变量,你可以用到如下的语法:

a += b

也就是说,你可以重载+=运算符,即实现__radd__函数来达到目的。

我们来看完整的例子:

class Yummy:
    def __init__(self, v):
        self.v = v

    def __add__(self, rhs):
        obj = Yummy(self.v)
        obj.v += rhs.v
        return obj

    def __radd__(self, rhs):
       # 修改自己
       self.v += rhs.v
      
       # 思考题:
       # 如果不返回self,会有什么副作用?为什么?
       return self

a = Yummy(10)
b = Yummy(8)

# 调用 __add__
c = a + b
a = c

# 调用 __radd__
a += c

在这个例子中,+(__add__)和+=(__radd__)的重载都遵循了惯例。

遵循惯例的好处是:在没有什么声明的情况情况下,代码的行为总是和它看起来的样子基本一致。

特别在大型工程中,特别是团队协作中,“惯例”显得尤为重要。如果一个代码的行为,总是如它所被期望的样子,我们的生活就会更美好,不是么!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值