在前面第十章以及第一章的时候介绍了Vector对象的运算符重载。第十三章专门介绍运算符重载。这里我们看几个之前没讲过的运算符__neg__,__pos__,__invert__
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __neg__(self):
        return "Vector(%d)" % (-self.x)
    def __str__(self):
        return "Vector(%s)" % (str(self.data))
    def __iter__(self):
        return iter(self.data)
    def __pos__(self):
        return "Vector(%d)" % (self.x+1)
    def __invert__(self):
        return "Vector(%d)" % (~self.x) if __name__=="__main__":
    v=Vector(1)
    print -v
    print +v
    print ~v
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
Vector(-1)
Vector(2)
Vector(-2)
__neg__是在-v的时候调用
__pos__是在+v的时候调用
__invert__是在~v的时候调用
下面我们重新来看下+运算符。我们想实现两个向量想加Vector([1,2,3])+Vector([1,2,3])得到[2,4,6].代码如下
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __add__(self, other):
        return [a+b for a,b in zip(self.x,other.x)]
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3])
    print v1+v2
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6]
在前面的例子中通过zip函数对两个列表进行配对得到元组。[(1,1),(2,2),(3,3)]然后迭代进行加法运算最终得到结果[2,4,6]
但是如果两个向量的格式变化一下。变成Vector([1,2,3])+Vector([1,2,3,4]).我们期望得到Vector([2,4,,6,4]), 向量Vector([1,2,3,4])由于比Vector([1,2,3])多一个数字,想加的时候最好是用零填充较短的那个向量。那么实际结果如何呢
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3,4])
    print v1+v2
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6]
我们看到结果和上次一样还是[2,4,6]和我们预想的[2,4,6,4]相差太大。原因在于zip得到的结果是[(1, 1), (2, 2), (3, 3)],并没有包含多出来的4。这种情况下我们就需要用到itertools.izip_lonest,这个方法会自动填充缺失的向量元素
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __add__(self, other):
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        return [a+b for a,b in pairs]
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3,4])
    print v1+v2
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6, 4]
在izip_longest中,fillvalue代表填充的值。本次例子中设置的是0.因此最后一位是0+4=4
 
接下来我们的需求继续变下。我们想实现Vector([1,2,3])+[1,2,3]也就是向量类和列表相加
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __add__(self, other):
        if type(other) == list:
            pairs=itertools.izip_longest(self.x,other,fillvalue=0)
        else:
            pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        return [a+b for a,b in pairs]
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3,4])
    print v1+[1,2,3,4]
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6, 4]
首先在__add__中判断other的属性,然后针对性的得到pairs
 
那如果是[1,2,3,4]+v1的结果会是什么呢?运行报错。
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
Traceback (most recent call last):
  File "E:/py_prj/fluent_python/chapter13.py", line 27, in <module>
    print [1,2,3,4]+v1
TypeError: can only concatenate list (not "Vector") to list
这是因为[1,2,3,4]并没有add方法,只有Vector才有,这种场景需要用到__radd__方法
具体的运算流程图如下:
1 如果a有__add__方法,则进行a.__add__(b)运算,否则返回Notimplemented然后检查b有没有__radd__方法。如果有,则调用b.__radd__(a),否则返回Notimplemented

我们在Vector中增加__radd__方法:
def __radd__(self, other):
    return self+other
__radd__直接委托__add__进行运算。代码也很简单。这下运算结果正确了。
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6, 4]
同样的方法也适用于乘法运算符__mul__和__rmul__。
 
下面来看下比较运算符以及反向比较在__eq__的作用。
首先定义__eq__的代码,首先比较长度,然后比较对应的元素,如果都相等则返回True,否则返回False
def __eq__(self, other):
    if type(other) == list or type(other)==tuple:
        pairs=itertools.izip_longest(self.x,other,fillvalue=0)
        length=len(other)
    else:
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        length=len(other.x)
    return (len(self.x)==length) and all(a==b for a,b in pairs)
if __name__=="__main__":
    v1=Vector([1.0,2.0,3.0])
    v2=Vector(range(1,4))
    print 'v1 compare v2 %s' % (v1==v2)
    v3=(1,2,3)
    print 'v1 compare v3 %s' % (v1==v3)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
v1 compare v2 True
v1 compare v3 True 
v1和v2相等这个没啥疑问,但是当用元组(1,2,3)和向量v1相比的时候,得到的结果也是True,我们如果用[1,2]==(1,2)得到的结果为False,因为列表和元组不是一个类型。那么为什么向量和元组相比却是相等的呢? 这就是问题所在了。在比较的时候并没有比较类型。代码更新如下:
def __eq__(self, other):
    if type(other) == list or type(other)==tuple:
        pairs=itertools.izip_longest(self.x,other,fillvalue=0)
        length=len(other)
    else:
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        length=len(other.x)
    if isinstance(other,Vector): #在这里判断是否属于向量类
        return (len(self.x)==length) and all(a==b for a,b in pairs)
    else:
        return NotImplemented
再继续看一个例子,增加一个Vector2d向量:
class Vector2d(object):
    def __init__(self,x):
        self.x=x
    def __eq__(self, other):
        return tuple(self.x) ==  tuple(other.x)
if __name__=="__main__":
    v1=Vector([1.0,2.0,3.0])
 
v4=Vector2d([1,2,3])
print v1==v4
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
True
得到的结果为True. Vector2d不属于Vector的实例呢。为什么会相等呢。这里用到了反向比较的方法
(1)首先Vector.__eq__(v1,v4),v4不是Vector的实例。返回Notimplemented。然后尝试调用Vector2d.__eq__(v4.v1)
(2)Vector2d.__eq__(v4.v1)把两个操作数都变成元组。然后比较。结果为True。
其实在用Vector和元组进行比较的时候,也是一样的首先Vector.__eq__(v1,v4),返回Notimplemented。接着开始调用tuple.__eq__(v3,v1),但是tuple不知道vector是什么,因此返回Notimplemented。对于==来说,如果反向调用也是Notimplemented,则会进行最后的尝试,比较对象的ID,如果仍然不相等则返回False。
 
接下来我们看下!=运算符
print v1!=v4
得到的结果是True,但是我们并没有实现__neq__的方法,也能得到对应的结果,其实__neq__不用实现,只要定义了__eq__的方法,__neq__则会自动对__eq__的结果取反。
 
最后来看下增量赋值运算符:
if __name__=="__main__":
    v1=Vector([1.0,2.0,3.0])
    print id(v1)⑴
    v2=Vector(range(1,4))
    v1+=v2⑵
    print v1
    print id(v1)⑶
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
25012400
[2.0, 4.0, 6.0]
25005672
在(1)中,得到的id(v1)=25012400。 
在(2)中,经过v1+=v2后。在(3)中打印id(v1)等于25005672.和之前第一步的不一样了,也就是创建了新的实例。
如果我们想实现就地运算,不生成新的实例,就在v1的基础上想加呢。这就需要实现__iadd__方法。当执行+=的时候会自动调用__iadd__方法,而如果没有实现__iadd__方法,则会自动调用__add__方法
def __iadd__(self, other):
    if type(other) == list:
        pairs=itertools.izip_longest(self.x,other,fillvalue=0)
    else:
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
    i=0
    for p in pairs:
        self.x[i]=p[0]+p[1]
        i+=1
    return self
在__iadd__方法中,返回的是self. 再来看下执行结果:
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
25274544
Vector([2.0, 4.0, 6.0])
25274544
得到的实例是同一样
												

流畅的python学习笔记:第十三章:重载运算符__add__,__iadd__,__radd__,__mul__,__rmul__,__neg__,__eq__,__invert__,__pos__的更多相关文章

  1. 流畅的python学习笔记:第二章

    第二章开始介绍了列表这种数据结构,这个在python是经常用到的结构 列表的推导,将一个字符串编程一个列表,有下面的2种方法.其中第二种方法更简洁.可读性也比第一种要好 str='abc' strin ...

  2. 流畅的python学习笔记:第一章

    这一章中作者简要的介绍了python数据模型,主要是python的一些特殊方法.比如__len__, __getitem__. 并用一个纸牌的程序来讲解了这些方法 首先介绍下Tuple和nametup ...

  3. 流畅的python学习笔记第七章:装饰器

    装饰器就如名字一样,对某样事物进行装饰过后然后返回一个新的事物.就好比一个毛坯房,经过装修后,变成了精装房,但是房子还是同样的房子,但是模样变了. 我们首先来看一个函数.加入我要求出函数的运行时间.一 ...

  4. 流畅的python学习笔记:第九章:符合python风格的对象

    首先来看下对象的表现形式: class People():     def __init__(self,name,age):         self.name=name         self.a ...

  5. Python学习笔记(十三)

    Python学习笔记(十三): 模块 包 if name == main 软件目录结构规范 作业-ATM+购物商城程序 1. 模块 1. 模块导入方法 import 语句 import module1 ...

  6. #Python学习笔记:1-3章 (基于《python编程,从入门到实践)

    第1-3章 这个文档是记录我学习python时一些学习笔记以及一些想法也可以称作复习笔记 第一章:起步这一章主要是从第一个"hello world"程序到python环境的搭建与配 ...

  7. [Python学习笔记][第七章Python文件操作]

    2016/1/30学习内容 第七章 Python文件操作 文本文件 文本文件存储的是常规字符串,通常每行以换行符'\n'结尾. 二进制文件 二进制文件把对象内容以字节串(bytes)进行存储,无法用笔 ...

  8. [Python学习笔记][第五章Python函数设计与使用]

    2016/1/29学习内容 第四章 Python函数设计与使用 之前的几页忘记保存了 很伤心 变量作用域 -一个变量已在函数外定义,如果在函数内需要修改这个变量的值,并将这个赋值结果反映到函数之外,可 ...

  9. [Python学习笔记][第四章Python字符串]

    2016/1/28学习内容 第四章 Python字符串与正则表达式之字符串 编码规则 UTF-8 以1个字节表示英语字符(兼容ASCII),以3个字节表示中文及其他语言,UTF-8对全世界所有国家需要 ...

随机推荐

  1. Spring中使用集成MongoDB Client启动时报错:rc: 48

    一定是所在的服务器也装了MongoDB导致端口冲突,解决方法:kill掉全部MongoDB的进程. ps aux | grep mongod PID 参考: http://blog.csdn.net/ ...

  2. 在红米note4上实现自动安装软件

    因为要做自动化测试,需要对已发布的包进行回归手测,这个时候需要手动安装APK,但是红米会弹出继续安装的按钮,手点一次比较烦,想自动点"继续安装"按钮! 感谢先行者们的分享 本文参考 ...

  3. 进程间通信(IPC)介绍(转)

    进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息. IPC的方式通常有管道(包括无名管道和命名管道).消息队列.信号量.共享存储.Socket ...

  4. 【Excle数据透视】如何创建一个数据透视表

    数据透视表可以汇总.分析.浏览和提供工作表数据或外部数据源的汇总数据 创建方法一 通过”插入”创建,具体操作请看下图: 创建方法二 通过快捷键创建 使用[ALT+D+P]创建 首先按下alt键,然后依 ...

  5. Linux是怎么启动的(整理)

    昨天笔试考了一道关于linux系统启动的过程,当时没答上来,现在整理出来(其实并不复杂). 按下电源按钮的直到欢迎页出来之后,linux总共做的事可以分为五步来完成. 1.  BIOS加电自检: 加电 ...

  6. SAS学习经验总结分享:篇四—SQL过程

    SQL过程 SQL过程是实现对数据集或关系数据库的表进行操作的过程,对数据集或关系数据库的表进行查询.修改.创建表.删除数据.插入数据和更新数据等功能.提现了SAS对大型数据库管理系统通用的SQL语言 ...

  7. template.js文档

    参见GitHub:https://github.com/yanhaijing/template.js/ template.js简介: template.js 一款javascript模板引擎,简单,好 ...

  8. jQuery 获取DOM元素

    (function (window){ var arr=[]; var VP=function(selector,context){ return new VP.fn.init(selector,co ...

  9. C语言批量数据到动态二维数组

    上一篇文章将文件读取放到静态创建的二维数组中,可是结合网络上感觉到今天的DT时代,这样批量大量读取一个上百行的数据,分配的内存是否可能由于大量的数据而产生溢出呢,近期一直研究里malloc函数.通过它 ...

  10. BCG菜单button的简单使用

    一,新建一个BCGprojectCBCGPMenuButton,基于对话框. 二.添加一个button,并关联一个CButton类型的变量m_btn1.然后手动将类型改CBCGPMenuButton成 ...