1

I have a Vector2 class:

class Vector2():
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector2(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vector2(self.x - other.x, self.y - other.y)

    def __mul__(self, other):
        return Vector2(self.x * other, self.y * other)

    def __neg__(self):
        return Vector2(-self.x, -self.y)

    def magnitude(self):
        return math.sqrt(self.x ** 2 + self.y ** 2)

    @classmethod
    def distance(self, v1, v2):
        return math.sqrt((v2.x - v1.x) ** 2 + (v2.y - v1.y) ** 2)

    def normalize(self):
        return self * (1/self.magnitude())

When I try to do 1.0 * Vector2(), I get the error: TypeError: unsupported operand type(s) for *: 'float' and 'instance'

However, sometimes it works as intended:

#this works as intended, s is a float
ball.pos -= ball.vel.normalize() * s

ball.vel is a vector and I am able to multiply by a float. Vectors are multiplied by floats in many sections of my code without errors.

Does anyone know where this inconsistency comes from?

Thanks

3
  • 3
    Are you sure you did Vector2() * 1.0 and not 1.0 * Vector2()? Commented Jul 14, 2018 at 23:32
  • Wow that was it. Why does it matter though? Commented Jul 14, 2018 at 23:34
  • because float * whatever is defined by float, whereas whatever * float is defined by whatever Commented Jul 14, 2018 at 23:36

2 Answers 2

1

Define an __rmul__ method to get a_float * a_vector working. It can be as simple as

def __rmul__(self, other):
    return self * other

The other operators also have a dunder-r version. These reflected operators are called when the normal version is not defined for the given types. See the docs for the NotImplemented builtin constant.

The expression a * b is equivalent to a.__mul__(b), unless b is an instance of a subclass of a's class or a.__mul__(b) returns NotImplemented, in which case it's b.__rmul__(a) instead.

Sign up to request clarification or add additional context in comments.

3 Comments

Even simpler: __rmul__ = __mul__. Obviously, this only works for commutative operators.
@dan04 good point, but what if a subclass overrides __mul__? Then __rmul__ would still use the old version!
@gilch: If a subclass overrides __mul__ and not __rmul__, that's the subclass's fault.
1

This is for anyone else who stumbles onto this question. I was learning Operator Overloading (to learn how SQLalchemy could possibly return a BinaryExpression from the addition of two columns). Anyway, my simple code looked like it should have worked. I was running it inside of a python playground (I won't name names) and it kept failing/would not even run. It gave me an error message 'Suspension error, cannot call a function that blocks or suspends here on line X in main.py'.

After 5 minutes, I tried another playground and I got what seemed like the same issue. It started to run without any error but didn't do anything and just timed out, so I figured it was the same deal - i.e. my code.

I spent 20 minutes reading S/O and trying to debug and try different combinations. The operator overload was not even being called (as I ran print('add called'). I was ready to pull my hair out but I tried a third playground, and everything worked as expected to a T, in all the combinations I thought it would.

I copied the same exact code to the other playgrounds and they continued to fail in the same way, so it wasn't the code after all. BTW it also worked in a local script.

I couldn't tell what version of Python these playgrounds were running, but I assume it was at least Python3. In order to give you more info on that, I tried to print(sys.version) (after importing sys of course), and even this failed in the same two playgrounds that failed, but worked in the one that worked. I don't know what their issues were but that's how it was.

The bottom line is you (apparently) can't trust these python playgrounds to work, even if they're near the top of your search results. WYSINWYG. I just wanted to put this out there in case anyone who needs this stumbles on it. This too is part of the journey.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.