Why can I still do addition with a subtraction magic method?

87 Views Asked by At

This isn't as much of a problem as it is a genuine question coming from a place of ignorance of how Python works under the veil.

I saw a bit on magic methods and decided to try writing some code with them to check out how exactly they were supposed to work and, through the trials and errors in which I did things wrong on purpose, I found that this code actually works:

class Vector:
  def __init__(self):
    self.n1 = 1

  def __sub__(self, n):
    return self.n1 + n

x = Vector()
print(x - 3)

Thing is, the code returns to me the value 4 - which means it is adding the two numbers instead of either telling me it can't solve this because I wrote the wrong sign or giving me a negative number as a result

At the same time, if I write the code like this:

class Vector:
  def __init__(self):
    self.n1 = 1

  def __sub__(self, n):
    return self.n1 + n

x = Vector()
print(x + 3)

... it automatically ceases working and says I'm doing something wrong (well, obviously, I'm trying to do addition in a subtraction method! It's not supposed to work!)

I was wondering why exactly Python even allowed my first slip-up to begin with. What is happening behind the veil for it to actually run?

2

There are 2 best solutions below

0
OM222O On

Python doesn't care about what you do in a function, that's the whole point of letting you define the operators. the first time you defined __sub__ which is called when you do x - y and you decided to do addition when __sub__ is called. The second time around you still defined __sub__ but not __add__, so python can't call __add__ when you do x + y

3
Savir On

I think you are looking at it wrong.

It turns out that Python is not very judgmental. Do you wanna add something in a method usually reserved for subtractions? So be it. Wanna print something instead? Aight. Wanna erase your hard drive? Up to you...

The only thing that Python is going to do when you write some_instance - blah, is calling the dunder method __sub__ of some_instance (because of the -), with some_instance as the first parameter (self) and blah as the second. What that method does is up to you. Do you wanna add (as you do in your example)? Surefinewhynot?. Would you rather print? Fine:

class Vector:
  def __sub__(self, n):
    print(f"Hello {n}")

x = Vector()
x - "folks"

will show:

Hello folks

As for your second question: Python doesn't know how to add an instance of Vector and the integer 3. Is not because you are doing addition on a subtraction method. The subtraction method (__sub__) is not called at all. It doesn't matter whether it's there or not. Is because you're trying to add two things that can't be added (or rather: two things that Python doesn't know how to add).

class Vector:
  pass

x = Vector()
x + 3

The error is quite telling

TypeError: unsupported operand type(s) for +: 'Vector' and 'int'

If you want to be able to add a Vector and an integer, you can always implement the dunder method __add__ in Vector, of course

Oh, and don't let the name Vector of your class fool you, though. Just because the class is called Vector, it doesn't mean it's going to behave like one.

class Somethingy:
  def __sub__(self, n):
    print(f"Hello {n}")

x = Somethingy()
x - "folks"