Python中的数学运算操作符使用进阶


Posted in Python onJune 20, 2016

Python中对象的行为是由它的类型 (Type) 决定的。所谓类型就是支持某些特定的操作。数字对象在任何编程语言中都是基础元素,支持加、减、乘、除等数学操作。
Python的数字对象有整数和浮点数,支持各种数学操作,比如+, -,*, /等。 没有这些操作符,程序中只能使用函数调用的方式进行数学运算,比如add(2, 3), sub(5, 2)。
程序中操作符的作用与普通数学操作的用法是一致的,使用中更加简便直观。Python中,这些操作符实现是通过定义一些object的特殊方法实现的,比如object.__add__()和object.__sub__()。如果用户在自己定义类时实现上述特殊方法,可以使自定义类的对象支持相应的数学操作,从而模拟数字对象的行为。这其实是达到了操作符重载的效果。

这里通过实现一个具有支持加法运算的中文数字类说明如何在Python中实现一个支持常见的数学操作。ChineseNumber类的基本定义如下。

class ChineseNumber:
  def __init__(self, n):
    self.num = n
    self.alphabet = [u'零', u'一', u'二', u'三', u'四', 
      u'五', u'六', u'七', u'八', u'九', u'十']

  def __str__(self):
    sign = '负' if self.num < 0 else ''
    return sign + ''.join([self.alphabet[int(s)] for s in str(abs(self.num))])

  def __repr__(self):
    return self.__str__()

目前,实现的效果是这样的:

>>> a = ChineseNumber(2)
>>> a  #调用a.__repr__()
二
>>> print(a)  #调用a.__str__()

二

一般数学操作符
定义类时,实现__add__()方法,可以给这个类增加+操作符。给ChineseNumber增加如下方法:

def __add__(self, other):
    if type(other) is ChineseNumber:
      return ChineseNumber(self.num + other.num)
    elif type(other) is int:
      return ChineseNumber(self.num + other)
    else:
      return NotImplemented

这时ChineseNumber的对象就可以使用+了。

>>> a, b = ChineseNumber(2), ChineseNumber(10)
>>> a + b
十二
>>> a + 5
七
>>> a + 3.7
TypeError: unsupported operand type(s) for +: 'ChineseNumber' and 'float'

对于+,a + b相当于调用a.__add__(b). 类似地,可以定义其他数学操作符,见下表。

object.__add__(self, other): +
object.__sub__(self, other): -
object.__mul__(self, other): *
object.__matmul__(self, other): @
object.__truediv__(self, other): /
object.__floordiv__(self, other): //
object.__mod__(self, other): %
object.__divmod__(self, other): divmod, divmod(a, b) = (a/b, a%b)
object.__pow__(self, other[,modulo]): **, pow()
object.__lshift__(self, other): <<
object.__rshift__(self, other): >>
object.__and__(self, other): &
object.__xor__(self, other): ^
object.__or__(self, other): |

操作数反转的数学操作符 (Reflected/Swapped Operand)

>>> 2 + a
TypeError: unsupported operand type(s) for +: 'int' and 'ChineseNumber'

2是整数类型,它的__add__()方法不支持ChineseNumber类的对象,所以出现了上述错误。定义操作数反转的数学操作符可以解决这个问题。给ChineseNumber类添加__radd__()方法,实现操作数反转的+运算。

def __radd__(self, other):
    return self.__add__(other)

对于a + b,如果a没有定义__add__()方法,Python尝试调用b的__radd__()方法。此时,a + b相当于调用b.__radd__(a)。

>>> a = ChineseNumber(2)
>>> 2 + a
四

类似地,可以定义其他操作数反转的数学操作符,见下表。

object.__radd__(self, other): +
object.__rsub__(self, other): -
object.__rmul__(self, other): *
object.__rmatmul__(self, other): @
object.__rtruediv__(self, other): /
object.__rfloordiv__(self, other): //
object.__rmod__(self, other): %
object.__rdivmod__(self, other): divmod, divmod(a, b) = (b/a, b%a)
object.__rpow__(self, other[,modulo]): **, pow()
object.__rlshift__(self, other): <<
object.__rrshift__(self, other): >>
object.__rand__(self, other): &
object.__rxor__(self, other): ^
object.__ror__(self, other): |

运算赋值操作符
运算赋值操作符使用单个操作符完成运算和赋值操作,比如a += b相当于调用a = a + b。为ChineseNumber增加__iadd__()方法,可以实现+=操作符。

def __iadd__(self, other):
    if type(other) is ChineseNumber:
      self.num += other.num
      return self
    elif type(other) is int:
      self.num += other
      return self
    else:
      return NotImplemented

此时,

>>> a, b = ChineseNumber(2), ChineseNumber(10)
>>> a += b
>>> a
十二
>>> a + 7
>>> a
十九

类似地,可以定义其他运算赋值操作符,见下表。

object.__iadd__(self, other): +=
object.__isub__(self, other): -=
object.__imul__(self, other): *=
object.__imatmul__(self, other): @=
object.__itruediv__(self, other): /=
object.__ifloordiv__(self, other): //=
object.__imod__(self, other): %=
object.__ipow__(self, other[,modulo]): **=
object.__ilshift__(self, other): <<=
object.__irshift__(self, other): >>=
object.__iand__(self, other): &=
object.__ixor__(self, other): ^=
object.__ior__(self, other): |=

一元数学操作符
一元数学操作符是只有一个操作数的运算,比如取负数的操作符-。-对应的特殊函数是__neg__()。为ChineseNumber添加__neg__()方法,

def __neg__(self):
    return ChineseNumber(-self.num)

此时,ChineseNumber对象就支持-操作了。

>>> a = ChineseNumber(5)
>>> -a
负五

其他一元运算符见下表。

object.__neg__(self): -
object.__pos__(self): +
object.__abs__(self): abs()
object.__invert__(self): ~
object.__complex__(self): complex()
object.__int__(self): int()
object.__float__(self): float()
object.__round__(self): round()
object.__index__(self): operator.index()
Python 相关文章推荐
快速入手Python字符编码
Aug 03 Python
浅析Python数据处理
May 02 Python
机器学习实战之knn算法pandas
Jun 22 Python
python调用其他文件函数或类的示例
Jul 16 Python
python matplotlib饼状图参数及用法解析
Nov 04 Python
python PIL/cv2/base64相互转换实例
Jan 09 Python
Python属性和内建属性实例解析
Jan 14 Python
PyTorch的SoftMax交叉熵损失和梯度用法
Jan 15 Python
pycharm实现在子类中添加一个父类没有的属性
Mar 12 Python
手把手教你安装Windows版本的Tensorflow
Mar 26 Python
Python通过文本和图片生成词云图
May 21 Python
keras实现多种分类网络的方式
Jun 11 Python
Python中在for循环中嵌套使用if和else语句的技巧
Jun 20 #Python
解析Python中的生成器及其与迭代器的差异
Jun 20 #Python
Python判断列表是否已排序的各种方法及其性能分析
Jun 20 #Python
Python编程中装饰器的使用示例解析
Jun 20 #Python
12步入门Python中的decorator装饰器使用方法
Jun 20 #Python
深入学习Python中的装饰器使用
Jun 20 #Python
Python中Iterator迭代器的使用杂谈
Jun 20 #Python
You might like
分页显示Oracle数据库记录的类之一
2006/10/09 PHP
php 计算两个时间戳相隔的时间的函数(小时)
2009/12/18 PHP
php urlencode()与urldecode()函数字符编码原理详解
2011/12/06 PHP
PHP APC的安装与使用详解
2013/06/13 PHP
基于php导出到Excel或CSV的详解(附utf8、gbk 编码转换)
2013/06/25 PHP
Linux下php5.4启动脚本
2014/08/03 PHP
[原创]php使用strpos判断字符串中数字类型子字符串出错的解决方法
2017/04/01 PHP
php 可变函数使用小结
2018/06/12 PHP
PHP中quotemeta()函数的用法讲解
2019/04/04 PHP
PHP下载大文件失败并限制下载速度的实例代码
2019/05/10 PHP
动态加载图片路径 保持JavaScript控件的相对独立性
2010/09/06 Javascript
javascript自启动函数的问题探讨
2013/10/05 Javascript
Node.js安装教程和NPM包管理器使用详解
2014/08/16 Javascript
Javascript 构造函数详解
2014/10/22 Javascript
浅谈react.js 之 批量添加与删除功能
2017/04/17 Javascript
vue之数据交互实例代码
2017/06/20 Javascript
微信小程序点击控件修改样式实例详解
2017/07/07 Javascript
JScript实现表格的简单操作
2017/08/15 Javascript
JavaScript基于对象方法实现数组去重及排序操作示例
2018/07/10 Javascript
vue项目中使用eslint+prettier规范与检查代码的方法
2020/01/16 Javascript
Vue生命周期activated之返回上一页不重新请求数据操作
2020/07/26 Javascript
nuxt 自定义 auth 中间件实现令牌的持久化操作
2020/11/05 Javascript
python 实现堆排序算法代码
2012/06/05 Python
Python实现求数列和的方法示例
2018/01/12 Python
python实现word 2007文档转换为pdf文件
2018/03/15 Python
Numpy数组转置的两种实现方法
2018/04/17 Python
pytorch动态网络以及权重共享实例
2020/01/06 Python
python爬虫利用代理池更换IP的方法步骤
2021/02/21 Python
京东奢侈品:全球奢侈品牌
2018/03/17 全球购物
老师对学生的寄语
2014/04/09 职场文书
军训拉歌口号
2014/06/13 职场文书
广告艺术设计专业自荐书
2014/07/08 职场文书
法人单位适用的授权委托书
2014/09/19 职场文书
2015年初三班主任工作总结
2015/05/21 职场文书
python实现腾讯滑块验证码识别
2021/04/27 Python
浅谈Redis的事件驱动模型
2022/05/30 Redis