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部署web开发程序的几种方法
May 05 Python
python字典操作实例详解
Nov 16 Python
Python paramiko模块的使用示例
Apr 11 Python
python django框架中使用FastDFS分布式文件系统的安装方法
Jun 10 Python
Django中在xadmin中集成DjangoUeditor过程详解
Jul 24 Python
基于Python中isfile函数和isdir函数使用详解
Nov 29 Python
将tensorflow.Variable中的某些元素取出组成一个新的矩阵示例
Jan 04 Python
appium+python adb常用命令分享
Mar 06 Python
Python3读写ini配置文件的示例
Nov 06 Python
python更新数据库中某个字段的数据(方法详解)
Nov 18 Python
详解python3类型注释annotations实用案例
Jan 20 Python
python中spy++的使用超详细教程
Jan 29 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
1982年日本摄影师镜头下的中国孩子 那无忧无虑的童年
2020/03/12 杂记
关于PHP中Object对象的笔记分享
2011/06/28 PHP
PHP求小于1000的所有水仙花数的代码
2012/01/10 PHP
PHP获取当前url的具体方法全面解析
2013/11/26 PHP
ThinkPHP路由机制简介
2016/03/23 PHP
Yii1.1框架实现PHP极光推送消息通知功能
2018/09/06 PHP
JQuery 应用 JQuery.groupTable.js
2010/12/15 Javascript
jQuery仿Excel表格编辑功能的实现代码
2013/05/01 Javascript
js正文内容高亮效果的实现方法
2013/06/30 Javascript
浅析document.ready和window.onload的区别讲解
2013/12/18 Javascript
$.each与$().each的区别示例介绍
2014/03/20 Javascript
深入分析escape()、encodeURI()、encodeURIComponent()的区别及示例
2014/08/04 Javascript
使用typeof判断function是否存在于上下文
2014/08/14 Javascript
js判断浏览器类型及设备(移动页面开发)
2015/07/30 Javascript
JS更改select内option属性的方法
2015/10/14 Javascript
JavaScript中使用sencha gridpanel 编辑单元格、改变单元格颜色
2015/11/26 Javascript
js实现图片上传并正常显示
2015/12/19 Javascript
AngularJS使用ng-repeat指令实现下拉框
2016/08/23 Javascript
详解node HTTP请求客户端 - Request
2017/05/05 Javascript
bootstrap fileinput插件实现预览上传照片功能
2018/01/23 Javascript
浅谈手写node可读流之流动模式
2018/06/01 Javascript
JS获取本地地址及天气的方法实例小结
2019/05/10 Javascript
js判断在哪个浏览器打开项目的方法
2020/01/21 Javascript
快速了解Vue父子组件传值以及父调子方法、子调父方法
2020/07/15 Javascript
[49:35]2018DOTA2亚洲邀请赛3月30日 小组赛A组 KG VS TNC
2018/03/31 DOTA
PYTHON 中使用 GLOBAL引发的一系列问题
2016/10/12 Python
python实现旋转和水平翻转的方法
2018/10/25 Python
pandas dataframe添加表格框线输出的方法
2019/02/08 Python
python实现替换word中的关键文字(使用通配符)
2020/02/13 Python
自主实习接收函
2014/01/13 职场文书
《匆匆》教学反思
2014/02/22 职场文书
电台实习生求职信
2014/02/25 职场文书
群众路线教育实践活动个人对照检查材料
2014/09/22 职场文书
常务副总经理岗位职责
2015/02/02 职场文书
物业客服专员岗位职责
2015/04/07 职场文书
机关干部纪律作风整顿心得体会
2016/01/23 职场文书