反射运算

  什么是反射运算符,其实就是反转了两个对象,下面先看一个普通运行符的实现:

  1. class Foo(object):
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. def __add__(self, other):
  6. return 'Foo:%s + %s' % (self.x, other.x)
  7.  
  8. class Boo(object):
  9. def __init__(self, x):
  10. self.x = x
  11.  
  12. def __add__(self, other):
  13. return 'Boo:%s + %s' % (self.x, other.x)
  14.  
  15. a = Foo(123)
  16. b = Boo(321)
  17. print a + b
  18. print b + a

  在普通的加法运算中,调用的是+号左边的__add__方法,调用谁谁就为self。所以左边是self,右边为other,所以结果如上。

  而反射运行其实就是交换这两者,下面看例子:

  1. class Foo(object):
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. def __radd__(self, other):
  6. return 'Foo:%s + %s' % (self.x, other.x)
  7.  
  8. class Boo(object):
  9. def __init__(self, x):
  10. self.x = x
  11.  
  12. def __radd__(self, other):
  13. return 'Boo:%s + %s' % (self.x, other.x)
  14.  
  15. a = Foo(123)
  16. b = Boo(321)
  17. print a + b
  18. print b + a

  首先,不同的地方是这里调用的+后右边的__radd__方法。然后本来是左边的为self的,现在变成了右边的为self。

  总结起来就是:普通的运算调用的是运算符左边的方法,而反射运算符调用的是右边的方法,调用的是谁的方法,谁就为self。

  这里有几点要注意的地方:

1.不支持同一个类的实例进行反射运算:

  1. class Foo(object):
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. def __radd__(self, other):
  6. return 'Foo:%s + %s' % (self.x, other.x)
  7.  
  8. a = Foo(123)
  9. b = Foo(321)
  10. print a + b
  11. print b + a

2.当一个类实现了__add__的时候,将会掩盖__radd__方法,也就是__add__的优先度更高:

  1. class Foo(object):
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. def __radd__(self, other):
  6. return 'Foo:%s + %s' % (self.x, other.x)
  7.  
  8. class Boo(object):
  9. def __init__(self, x):
  10. self.x = x
  11.  
  12. def __add__(self, other):
  13. return 'Boo add:%s + %s' % (self.x, other.x)
  14.  
  15. def __radd__(self, other):
  16. return 'Boo radd:%s + %s' % (self.x, other.x)
  17.  
  18. a = Foo(123)
  19. b = Boo(321)
  20. print a + b
  21. print b + a

其逻辑如下:

  首先a + b,python看到了 a 中没有 __add__方法(忽略了__radd__),就去 b 中找__radd__(而不是__add__),因为在右边找的时候,就意味要使用反射运算了。所以最后得到了这个结果

  然后是b + a,python看到了 b 中有 __add__方法,就直接调用了它,不管 a 的内部是如何的。

基本反射运算就是这么一回事,下面是一些总结:

  • __radd__(self, other)

  • 反射加法

  • __rsub__(self, other)

  • 反射减法的

  • __rmul__(self, other)

  • 反射除法

  • __rfloordiv__(self, other)

  • 反射地板除,使用//运算符的

  • __rdiv__(self, other)

  • 反射除法,使用/运算符的.

  • __rtruediv__(self, other)

  • 反射真除.注意只有from __future__ import division 的时候它才有效

  • __rmod__(self, other)

  • 反射取模运算,使用%运算符.

  • __rdivmod__(self, other)

  • 长除法,使用divmod()内置函数,当divmod(other,self)时被调用.

  • __rpow__

  • 反射乘方,使用**运算符的

  • __rlshift__(self, other)

  • 反射左移,使用<<操作符.

  • __rrshift__(self, other)

  • 反射右移,使用>>操作符.

  • __rand__(self, other)

  • 反射位与,使用&操作符.

  • __ror__(self, other)

  • 反射位或,使用|操作符.

  • __rxor__(self, other)

  • 反射异或,使用^操作符.

增量运算

  所谓的增量运算,其实就是 x += 1 这样的形式,下面是几个例子:

  1. class Foo(object):
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. def __iadd__(self, other):
  6. return 'Foo iadd: %s + %s' % (self.x, other)
  7.  
  8. a = Foo(123)
  9. a += 1
  10. print a

  但是,如果两个对象的实现了__iadd__,情况就会大为不同:

  1. class Foo(object):
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. def __iadd__(self, other):
  6. return 'Foo iadd: %s + %s' % (self.x, other.x)
  7.  
  8. class Boo(object):
  9. def __init__(self, x):
  10. self.x = x
  11.  
  12. def __iadd__(self, other):
  13. return 'Boo iadd: %s + %s' % (self.x, other.x)
  14.  
  15. a = Foo(123)
  16. b = Boo(321)
  17. a += b
  18. print a

  看似很正常,然而代码如下时:

  1. a = Foo(123)
  2. b = Boo(321)
  3. a += b
  4. print a
  5. b += a
  6. print b

 

  报错显示:str没有x这个属性,但是按照代码来看,两个对象都有x属性呀。

  在b += a 这行有错,也就是说self为 b,other为 a。后来试验了一番,发现将:

   return 'Boo iadd: %s + %s' % (self.x, other.x)

  改为:

   return 'Boo iadd: %s + %s' % (self.x, other)

  代码就不会报错了,但是输出几个如下:

  很奇怪,other变成了a中__iadd__的返回值了,也就是说当a调用了__iadd__方法之后,在将其用在其他的增量运算时,other不在代表a对象本身,而是其__iadd__的返回值。

  当我们回归其本质:x += 1 ==> x = x + 1 可以看出,x 其实进行了重新赋值,重新赋值成了 __iadd__ 的返回值。而我们代码示例中,这个方法的返回值是一个字符串。在一开始时,x是我们类的实例。但是在进行了增量运算后,x 变成了魔法方法的返回值,也就是字符串了,所以才会出现以上的报错。

  所以我们在使用的时候要注意 x 身份的改变,不然会有许多意想不到的麻烦。

关于增量方法的总结:

  • __iadd__(self, other)

  • 加法赋值

  • __isub__(self, other)

  • 减法赋值.

  • __imul__(self, other)

  • 乘法赋值

  • __ifloordiv__(self, other)

  • 整除赋值,地板除,相当于 //= 运算符.

  • __idiv__(self, other)

  • 除法赋值,相当于 /= 运算符.

  • __itruediv__(self, other)

  • 真除赋值,注意只有你 whenfrom __future__ import divisionis,才有效.

  • __imod_(self, other)

  • 模赋值,相当于 %= 运算符.

  • __ipow__

  • 乘方赋值,相当于 **= 运算符.

  • __ilshift__(self, other)

  • 左移赋值,相当于 <<= 运算符.

  • __irshift__(self, other)

  • 左移赋值,相当于 >>= 运算符.

  • __iand__(self, other)

  • 与赋值,相当于 &= 运算符.

  • __ior__(self, other)

  • 或赋值,相当于 |= 运算符.

  • __ixor__(self, other)

  • 异或运算符,相当于 ^= 运算符.


  欢迎大家交流。

  参考资料:戳这里

python魔法方法-反射运算和增量运算的更多相关文章

  1. python魔法方法大全

    1.python魔法方法详解: python魔法方法是可以修改重载的,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而 ...

  2. with上下文管理 python魔法方法

    with语法在Python里很常见, 主要的利好是使用代码更简洁. 常见的使用场景有: 1. 资源对象的获取与释放. 使用with可以简化try...finally ... 2. 在不修改函数代码的前 ...

  3. python 魔法方法诠释

    什么是Python魔法方法 什么是魔法方法呢?它们在面向对象的Python的处处皆是.它们是一些可以让你对类添加"魔法"的特殊方法. 它们经常是两个下划线包围来命名的(比如 ini ...

  4. Python魔法方法总结及注意事项

    1.何为魔法方法: Python中,一定要区分开函数和方法的含义: 1.函数:类外部定义的,跟类没有直接关系的:形式: def func(*argv): 2.方法:class内部定义的函数(对象的方法 ...

  5. python 魔法方法补充(__setattr__,__getattr__,__getattribute__)

    python 魔法方法补充 1 getattribute (print(ob.name) -- obj.func())当访问对象的属性或者是方法的时候触发 class F(object): def _ ...

  6. 1. Python 魔法方法

    Python 魔法方法 基础: 如果你想... 所以,你写... Python调用... 初始化一个实例 x = MyClass() x.__init__() 作为一个字符串的"官方&quo ...

  7. python魔法方法:__getattr__,__setattr__,__getattribute__

    python魔法方法:__getattr__,__setattr__,__getattribute__ 难得有时间看看书....静下心来好好的看了看Python..其实他真的没有自己最开始想的那么简单 ...

  8. python魔法方法-单目运算及一般算数运算

    在比较的魔法方法中,我们讨论了魔法方法其实就是重载了操作符,例如>.<.==等.而这里,我们继续讨论有关于数值的魔法方法. 1.单目运算符或单目运算函数 __pos__(self) 实现一 ...

  9. python 魔法方法

    I am not a creator, I just a porter. Note: Everything is object in python. 对于Python来说一切都是对象,也就是函数的参数 ...

随机推荐

  1. SpringBoot注册Servlet、Filter、Listener

    SpringBoot默认是以jar包的方式启动嵌入式的Servlet容易来启动SpringBoot的Web应用,没有web.xml文件 因此我们可以使用以下方式来注册Servlet.Filter.Li ...

  2. HTML学习笔记09-列表

    HTML支持无序,有序,自定义列表 列表项内部可以使用段落.换行符.图片.连接.以及其他列表等 无序列表 无序列表使用粗体圆点(典型的小黑圆圈)进行标记,列表始于<ul>标签,列表项使用& ...

  3. 017_nginx重定向需求

    重定向的各种需求 需求一. 前端同事需要把特定的url进行重定向,实现如下: location / { root /data/base.apiportal_opsweb; index index.ht ...

  4. MariaDB:在Linux下修改编码

    参考网址:http://www.cnblogs.com/vingi/articles/4302330.html: # vi /etc/my.cnf [mysqld] init_connect='SET ...

  5. [Java]求文件大小并保留两位小数(文件大小是一个长整型数单位是Byte)

    前言 为了获得一堆apk的大小,并与人类友好方式显示.本来是打算用以下方法,到时不能具体到保留两位小数. org.apache.commons.io.FileUtils.byteCountToDisp ...

  6. 破解idea

    2019最新注册码 地址:  http://idea.lanyus.com/ https://blog.csdn.net/best_luxi/article/details/81479820

  7. 【转】js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  8. [翻译] 一个kubernetes网络简明教程[Part 1]

    一个kubernetes网络简明教程[Part 1] 翻译: icebug 所有我学到的关于kubernetes网络的事情 你可能已经在kubernetes集群当中跑了一堆服务并且正在享受其带来的好处 ...

  9. PHP递归排序

    递归算法对于任何一个编程人员来说,应该都不陌生.因为递归这个概念,无论是在PHP语言还是Java等其他编程语言中,都是大多数算法的灵魂. 对于PHP新手来说,递归算法的实现原理可能不容易理解.但是只要 ...

  10. django----Form扩展

    用第二种方式需要加上下面的这个: 三.判断用户民是不存在,存在就不添加了 from django.core.exceptions import ValidationError initial  修改时 ...