2016-09-21 171 views
1

我目前正试图了解在使用a+ba.__add__(b)之间的区别是什么时候涉及到自定义类。有很多网站表示使用'+' - 运算符会导致使用特殊方法__add__ - 目前为止这很好。a + b和.__之间的区别__(b)

但是,当我运行下面的例子,我得到了两个不同的结果。

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知道检查对象C的__radd__并执行其中的代码。

为什么1+c结果在执行__radd__代码,但其他两个版本都只是返回NotImplemented不检查__radd__

+7

呃,因为你直接调用了__add__?正如你所说的,当你使用'+'运算符时,Python只会回退。 –

+0

猜测我不知道如果直接使用'__add__',则不会使用回退。 – Korred

回答

3

a+b相当于import operator; operator.add(a,b)。它首先呼叫a.__add__(b),然后,如有必要,b.__radd__(a)。但是ifsubclass(type(b), type(a)),然后b.__radd__(a)被称为第一。基于

docs on "special" methods

  • 关于__add__()

    __add__()被调用来实现二进制算术 “+” 操作。例如,要评估表达式x + y,其中x是具有__add__()方法的类的实例,则调用x.__add__(y)

    如果其中一种方法不支持使用提供的参数进行操作,则应返回未实现

  • 关于__radd__()

    如果左操作数不支持相应的操作和操作数是不同类型的这些功能只调用。例如,要评估表达式x + y,其中y是具有方法的类的实例,如果x.__add__(y)返回NotImplemented,则调用y.__radd__(x)

    如果右操作数的类型是左操作数类型的子类,并且该子类提供操作的反射方法,则将在左操作数的非反射方法之前调用此方法。这种行为允许子类覆盖其祖先的操作。

基于行为的例子说明:

案例1:

>>> print 1+c 
('C.__radd__', <__main__.C instance at 0x7ff5631397a0>, 1) 
reversed result 

这些功能radd如果左操作数不支持只叫相应的操作和操作数是不同的类型。在这种情况下,1不支持该类的添加,因此,它回退到C类的__radd__()函数。如果__radd__不是在C()类实现,它会回落到__add__()

案例2:

>>> 1 .__add__(c) 
NotImplemented 
>>> c .__add__(1) 
('C.__add__', <__main__.C instance at 0x7ff563139830>, 1) 
'result' 

1 .__add__(c)NotImplemented1int型和int类的add不支持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:的case 3

>>> 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) 

反之亦然。从堆栈跟踪中清除,__add__预期调用类的对象作为第一个参数,失败则导致异常。

+0

因此,当使用负责回退的'+'运算符时,基本上会发生更多幕后现象? – Korred

+0

更新了答案。我尽力解释它是如何工作的。如有疑问,请告诉我。我将进一步编辑并尝试更清晰 –

+0

也许我应该解释我的问题 - 使用'+'运算符和__add__函数有什么区别? 阅读有关特殊方法,它看起来像没有 - 但显然'NotImplemented'情况处理不同 – Korred