我目前正在尝试了解使用a+b
和a.__add__(b)
之间的区别在于自定义类。有很多网站都说使用'+' - 运算符会导致使用特殊方法__add__
- 到目前为止这很好。
但是当我运行以下示例时,我会得到两个不同的结果。
class C:
def __add__(self, other):
print("C.__add__", self, other)
return "result"
def __radd__(self, other):
print("C.__radd__", self, other)
return "reversed result"
c = C()
print(1+c)
print()
print(1 .__add__(c))
print(int.__add__(1,c))
结果:
C.__radd__ <C object at 0x7f60b92e9550> 1
reversed result
NotImplemented
NotImplemented
根据我的理解,当执行1+c
Python检查/执行int __add__
方法时 - 发现没有添加int和C对象的实现 - 返回NotImplemented - 这让Python知道检查__radd__
的对象C并执行其中的代码。
为什么1+c
导致执行__radd__
代码,但另外两个版本只返回NotImplemented
而不检查__radd__
??
答案 0 :(得分:3)
a+b
相当于import operator; operator.add(a,b)
。首先调用a.__add__(b)
,然后根据需要调用b.__radd__(a)
。但ifsubclass(type(b), type(a))
,则先调用b.__radd__(a)
。
关于__add__()
:
调用
__add__()
来实现二进制算术“+”操作。例如,要计算表达式x + y,其中x是具有__add__()
方法的类的实例,则调用x.__add__(y)
。如果其中一个方法不支持使用提供的参数进行操作,则应返回 NotImplemented 。
关于__radd__()
:
仅当左操作数不支持相应操作且操作数具有不同类型时才调用这些函数。例如,要计算表达式x + y,其中y是具有
__radd__()
方法的类的实例,如果y.__radd__(x)
返回NotImplemented,则调用x.__add__(y)
。如果右操作数的类型是左操作数类型的子类,并且该子类提供了操作的反射方法,则此方法将在左操作数的非反射方法之前调用。此行为允许子类覆盖其祖先的操作。
基于行为的示例说明:
案例1:
>>> print 1+c
('C.__radd__', <__main__.C instance at 0x7ff5631397a0>, 1)
reversed result
仅当左操作数不支持相应操作且操作数具有不同类型时,才会调用这些函数radd
。在这种情况下,1
不支持添加类,因此它会回退到__radd__()
类的C
函数。如果__radd__
类中没有实现C()
,那么它会回落到__add__()
<强>情况2:强>
>>> 1 .__add__(c)
NotImplemented
>>> c .__add__(1)
('C.__add__', <__main__.C instance at 0x7ff563139830>, 1)
'result'
1 .__add__(c)
提供NotImplemented
1
属于int
类型,add
类int
类不支持add
C类对象。但是c .__add(1)
运行是因为C()
类支持它。
案例3:
>>> int.__add__(1, c)
NotImplemented
>>> C.__add__(c, 1)
('C.__add__', <__main__.C instance at 0x7ff5610add40>, 1)
'result'
与case 2
相似。但是在这里,调用是通过类进行的,第一个参数作为该类的对象。行为是一样的。
案例4:
>>> int.__add__(c, 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__add__' requires a 'int' object but received a 'instance'
>>> C.__add__(1, c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method __add__() must be called with C instance as first argument (got int instance instead)
case 3
反之亦然。从堆栈跟踪中清除,__add__
期望调用类的对象作为第一个参数,失败导致异常。