I recently had to complete an assignment that used a lot of coordinate operations. Thinking to save time and simplify my code, I defined a class to encapsulate the behaviour of a coordinate pair. The class looked like this:
class Vector (tuple) :
def __init__ (self, value) :
tuple.__init__ (self, value)
def __add__ (self, other) :
return Vector ((self [0] + other [0], self [1] + other [1]))
This allowed me to write code like this (for example):
def translate (pointList, displacement) :
return [point + displacement for point in pointList]
But my application was terribly slow. Much slower than other assignments. I couldn't locate any inefficiency in my implementation of the algorithm, so I did a simple test to see what the overhead was of the Vector class. I expected somewhere between 5% and 15%.
My test of the Vector class looked like this:
v = Vector ((0, 0))
d = Vector ((1, -1))
loopIdx = 3000000
while loopIdx > 0 :
v = v + d
loopIdx -= 1
print (v)
This runs (typically) in this kind of time:
real 0m8.440s
user 0m8.367s
sys 0m0.016s
For comparison I ran this code:
v = (0, 0)
dX = 1
dY = -1
loopIdx = 3000000
while loopIdx > 0 :
v = ( v [0] + dX, v [1] + dY )
loopIdx -= 1
print (v)
Run time for this code is:
real 0m1.004s
user 0m0.995s
sys 0m0.006s
Have I done something seriously wrong, or does using class objects in Python really mean your application will take over 8 times as long to run?
Not really an answer how to make you class faster, but more of an alternative.
Instead of subclassing tuple
and writing all those add
, sub
etc. methods yourself, you cold just use Python's bultin complex
number type for 2D coordinates, which has all those operations already built in, correct and and super-fast.
>>> %timeit vector_no_init()
1 loops, best of 3: 1.39 s per loop
>>> %timeit plain_tuple()
1 loops, best of 3: 403 ms per loop
>>> %timeit complex_nums()
1 loops, best of 3: 171 ms per loop
For rotation, you can use complex multiplication: Just multiply your complex coordinate with a complex number that has, in polar form, absolute value 1 and a phase equal to the angle you want to rotate by.
To rotate by 90 degrees, you can just multiply by 1j
(anti-clockwise) or -1j
(clockwise). For all other angles, use the cmath
module for translation to and from polar form.
>>> c = complex(4, 2)
>>> c * cmath.rect(1, math.radians(45))
(1.4142135623730954+4.242640687119285j)
However, I would suggest not to subclass complex
to make rotate
a method of that class, because in this case you will have to overwrite all the other methods, like add
, as well, otherwise the result of addition will be a regular complex number, not providing the rotate
method. And this would undo all those performance gains, making it just as slow as your Vector
class. Instead, just make a function rotate(complex, angle) -> complex
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With