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中文问题解决方法(总结了多位前人经验,初学者必看)
Mar 13 Python
压缩包密码破解示例分享(类似典破解)
Jan 17 Python
Python脚本实现网卡流量监控
Feb 14 Python
Python删除Java源文件中全部注释的实现方法
Aug 30 Python
Python三种遍历文件目录的方法实例代码
Jan 19 Python
浅谈pandas中shift和diff函数关系
Apr 08 Python
Pandas标记删除重复记录的方法
Apr 08 Python
python3获取当前文件的上一级目录实例
Apr 26 Python
Python获取指定字符前面的所有字符方法
May 02 Python
python实现猜拳游戏
Mar 04 Python
python实现opencv+scoket网络实时图传
Mar 20 Python
Python操作CSV格式文件的方法大全
Jul 15 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
Discuz 5.0 中读取纯真IP数据库函数分析
2007/03/16 PHP
php获取远程图片的两种 CURL方式和sockets方式获取远程图片
2011/11/07 PHP
Ecshop 后台添加新功能栏目及管理权限设置教程
2017/11/21 PHP
Javascript入门学习资料收集整理篇
2008/07/06 Javascript
基于Jquery的文字自动截取(提供源代码)
2011/08/09 Javascript
JavaScript中的null和undefined解析
2012/04/14 Javascript
js实现类似jquery里animate动画效果的方法
2015/04/10 Javascript
Bootstrap每天必学之工具提示(Tooltip)插件
2016/04/26 Javascript
Ajax实现不刷新取最新商品
2017/03/01 Javascript
node.JS md5加密中文与php结果不一致的解决方法
2017/05/05 Javascript
react-redux中connect()方法详细解析
2017/05/27 Javascript
vue-cli3+ts+webpack实现多入口多出口功能
2019/05/30 Javascript
JavaScript中callee和caller的区别与用法实例分析
2019/06/28 Javascript
Python实现针对中文排序的方法
2017/05/09 Python
用于业余项目的8个优秀Python库
2018/09/21 Python
详解Python3中setuptools、Pip安装教程
2019/06/18 Python
django rest framework 实现用户登录认证详解
2019/07/29 Python
Python检查图片是否损坏及图片类型是否正确过程详解
2019/09/30 Python
浅析python表达式4+0.5值的数据类型
2020/02/26 Python
记一次pyinstaller打包pygame项目为exe的过程(带图片)
2020/03/02 Python
解决pip安装tensorflow中出现的no module named tensorflow.python 问题方法
2021/02/20 Python
canvas基础之图形验证码的示例
2018/01/02 HTML / CSS
如何高效率的查找一个月以内的数据
2012/04/15 面试题
网上常见的一份Linux面试题(多项选择部分)
2014/09/09 面试题
Java如何获得ResultSet的总行数
2016/09/03 面试题
理货员的岗位职责
2013/11/23 职场文书
《一本男孩子必读的书》教学反思
2014/02/19 职场文书
大学生怎样写好自荐信
2014/02/25 职场文书
《放飞蜻蜓》教学反思
2014/04/27 职场文书
员工廉洁自律承诺书
2014/05/26 职场文书
大专生自荐书范文
2014/06/22 职场文书
会计电算化实训报告
2014/11/04 职场文书
稽核岗位职责
2015/02/10 职场文书
首次购房证明
2015/06/19 职场文书
《窃读记》教学反思
2016/02/18 职场文书
VUE之图片Base64编码使用ElementUI组件上传
2022/04/09 Vue.js