Difference between a+b and a.__add__(b)

后端 未结 1 609
一向
一向 2020-12-06 15:14

I am currently trying to understand where the difference between using a+b and a.__add__(b) is when it comes to custom classes. There are numerous

相关标签:
1条回答
  • 2020-12-06 15:55

    a+b is equivalent to import operator; operator.add(a,b). It starts by calling a.__add__(b) and then, if necessary, b.__radd__(a). But ifsubclass(type(b), type(a)), then b.__radd__(a) is called first.

    Based on the docs on "special" methods:

    • Regarding __add__():

      __add__() is called to implement the binary arithmetic "+" operation. For instance, to evaluate the expression x + y, where x is an instance of a class that has an __add__() method, x.__add__(y) is called.

      If one of those methods does not support the operation with the supplied arguments, it should return NotImplemented.

    • Regarding __radd__():

      These functions are only called if the left operand does not support the corresponding operation and the operands are of different types. For instance, to evaluate the expression x + y, where y is an instance of a class that has an __radd__() method, y.__radd__(x) is called if x.__add__(y) returns NotImplemented.

      If the right operand’s type is a subclass of the left operand’s type and that subclass provides the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations.

    Explanation with the examples based on the behaviour:

    Case 1:

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

    These functions radd are only called if the left operand does not support the corresponding operation and the operands are of different types. In this case, 1 does not support addition of the class hence, it falls back to the __radd__() function of the C class. In case __radd__ was not implement in C() class, it would have fallen back to __add__()

    Case2:

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

    1 .__add__(c) gives NotImplemented as 1 is of int type and add of int class do not supports add with C class object. But c .__add(1) run because C() class supports that.

    Case 3:

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

    Similar to case 2. But here, the call is made via class with first argument as object of that class. Behaviour would be same.

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

    Vice-versa of case 3. As is cleared from the stack-trace, __add__ expected the object of the calling class as the first argument, failing which resulted in exception.

    0 讨论(0)
提交回复
热议问题