Почему при вызове Python "магический метод" не выполняет преобразование типов, как это было бы для соответствующего оператора?

Когда я 1-2.0 число с плавающей точкой из целого числа (например, 1-2.0), Python делает неявное преобразование типов (я думаю). Но когда я называю то, что считал одной и той же операцией, используя магический метод __sub__, это неожиданно __sub__ быть.

Что мне здесь не хватает? Когда я перегружаю операторы для своих собственных классов, есть ли способ обойти это, кроме явного приведения ввода к тому типу, который мне нужен?

a=1
a.__sub__(2.)
# returns NotImplemented
a.__rsub__(2.)
# returns NotImplemented
# yet, of course:
a-2.
# returns -1.0

Ответ 1

a - b - это не просто a.__sub__(b). Он также пытается b.__rsub__(a) если a не может обработать операцию, а в случае 1 - 2. это float __rsub__ который обрабатывает операцию.

>>> (2.).__rsub__(1)
-1.0

Вы запустили a.__rsub__(2.), Но это неправильно __rsub__. Вам нужен правый операнд __rsub__, а не левый операнд.


Не существует неявного преобразования типов, встроенного в оператор вычитания. float.__rsub__ должен обрабатывать int вручную. Если вы хотите преобразование типов в своих собственных реализациях операторов, вам придется обрабатывать это также вручную.

Ответ 2

@user2357112 уже хорошо сказал, но нет ничего похожего на пример.

class A:
   def __sub__(self, other):
       print('A.__sub__')
       if not isinstance(other, A):
           return NotImplemented
       return 0

   def __rsub__(self, other):
       print('A.__rsub__')
       if not isinstance(other, A):
           return NotImplemented
       return 0

class B:
   def __sub__(self, other):
       print('B.__sub__')
       if not isinstance(other, B):
           return NotImplemented
       return 0

a1 = A()
a2 = A()
b = B()

a1 - a2
A.__sub__
# 0

Объекты a1 и a2 совместимы (оба типа A), верный результат возвращается.

Далее рассмотрим,

b - a1
B.__sub__
A.__rsub__
# TypeError: unsupported operand type(s) for -: 'B' and 'A'

Объекты b и a1 не совместимы. Сначала b.__sub__, который возвращает NotImplemented, поэтому a1.__rsub__, который также возвращает NotImplemented. Итак, TypeError.

В заключение,

a1 - b
A.__sub__
# TypeError: unsupported operand type(s) for -: 'A' and 'B'

На этот раз a1.__sub__, что возвращает NotImplemented. Теперь, так как b.__rsub__ не определен, TypeError приподнята.