Reputation: 55
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
Upvotes: 1
Views: 1196
Reputation: 463
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.
Upvotes: 1
Reputation: 11651
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.
Upvotes: 1