This is H1rono.
I made an immutable (immutable) type in pure python. I hope I can tell you in detail what I did.
Make a vector type. Specifically,
MyVector
length
and the direction as direction
(degree) (do not save the coordinates)MyVector (length = size, direction = direction)
in the interpreterproperty
named x
, y
(eg vector.x
)+
and -
.*
*
, the size of the vector is multiplied by that real number./
, the size of the vector divided by the real number is returned.Create a class with such requirements.
First, implement the "immutable" requirement.
Immutable quote from Official Glossary
immutable (Immutable) An object with a fixed value. Immutable objects include numbers, strings, and tuples. The values of these objects cannot be changed. If you want to remember another value, you have to create a new object. Immutable objects play an important role in situations where fixed hash values are required. A dictionary key is an example.
In other words, it seems that you cannot say vector.direction = 256
.
What's great is the named tuple in the built-in collections module. With the 3 / library / collections.html # collections.namedtuple) function, you can easily create a subclass of tuple
(that is, immutable) with a name assigned to that element. python is amazing.
vec.py
from collections import namedtuple
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
pass #Meet other requirements here
if __name__ == '__main__':
vec = MyVector(4, 2)
print(vec) #MyVector(length=4, direction=2)
Execution result:
MyVector(length=4, direction=2)
Now, among the requirements mentioned above,
MyVector
length
and the direction as direction
(degree) (do not save the coordinates)MyVector (length = size, direction = direction)
in the interpreterWas able to be implemented for the time being. The remaining requirements are
property
named x
, y
+
and -
.*
*
, the size of the vector is multiplied by that real number./
, the size of the vector divided by the real number is returned.is. It has decreased all at once.
The following implements the requirement that "coordinates as a position vector can be referenced by property
named x
, y
". Use the built-in math module.
The expression for x
islength * cos (pi * direction / 180)
, and the expression for y
is length * sin (pi * direction / 180)
.
vec.py
from collections import namedtuple
from math import pi, cos, sin #Add import
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Additional part from here#
@property
def x(self):
theta = pi * self.direction / 180
return self.length * cos(theta)
@property
def y(self):
theta = pi * self.direction / 180
return self.length * sin(theta)
#Additional part so far#
if __name__ == '__main__':
from math import sqrt
root2 = sqrt(2)
vec = MyVector(root2, 45)
print('x = {}'.format(vec.x)) #x = 1
print('y = {}'.format(vec.y)) #y = 1
As you can see by executing the above code, the execution result is not as commented. In my environment,
x = 1.0000000000000002
y = 1.0
It will be displayed. This behavior is probably related to here (floating point arithmetic, its problems and limitations). think. If you are interested, please. Anyway, for the time being
property
named x
, y
Was implemented. The rest,
+
and -
.*
*
, the size of the vector is multiplied by that real number./
, the size of the vector divided by the real number is returned.is. It's an implementation of all operations.
Reference: Official Reference (emulates numeric types)
The correspondence between the operators and method names implemented this time is roughly as shown in the following table.
operator | Method name |
---|---|
+ |
__add__ , __radd__ |
- |
__sub__ , __rsub__ |
* |
__mul__ , __rmul__ |
/ |
__truediv__ , __rtruediv__ |
All functions have (self, other)
arguments. In other words, when vector = Myvector (8, 128)
, vector + x
callsvector .__ add__ (x)
and returns the result as a calculation result.
Methods with r
at the beginning, such as __radd__
, implement "the operands are reflected (replaced)" (quoted from the URL above). That is, as x + vector
, whenx.__ add__ (vector)
returns Not Implemented
, the result of vector.__radd__ (x)
is returned. Since it's a big deal, I'll add this one as well.
The implementation method is like this. Let's write it.
I would like to say that, in the implementation of addition and subtraction, it is necessary to obtain the vector represented by (length, direction)
from the coordinates of (x, y)
. First, implement that method.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos #Add import
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Additional part from here#
@classmethod
def from_coordinates(cls, p1, p2=None):
#p1: (x, y)
#p2: (x, y)Or None
if p2 is None:
#Treat as a position vector
x, y = p1
else:
#Starting from p1,Treat p2 as an endpoint vector
x = p2[0] - p1[0]
y = p2[1] - p1[1]
r = sqrt(x**2 + y**2)
if r == 0:
#0 vector
return cls(0, 0)
#Inverse trigonometric function
theta = 180 * acos(x / r) / pi
if y < 0:
# 180 < direction < 360
#theta=32 -> direction=328
#theta=128 -> direction=232
theta = 360 - theta
return cls(r, theta)
#Additional part so far#
@property
def x(self):
theta = pi * self.direction / 180
return self.length * cos(theta)
@property
def y(self):
theta = pi * self.direction / 180
return self.length * sin(theta)
if __name__ == '__main__':
vec = MyVector.from_coordinates((2, 2))
print('x = {}'.format(vec.x)) #x = 2
print('y = {}'.format(vec.y)) #y = 2
Added the from_coordinates
function. As the name implies, it creates a MyVector
object from the coordinates. I chose the class method because I found it very convenient. Execution result:
x = 1.9999999999999996
y = 2.0000000000000004
See you later ... You can add the round
function and round it later. Anyway, I was able to get the vector represented by (length, direction)
from the coordinates of (x, y)
.
Next, we will implement addition and subtraction. Since the supported calculation target is a vector of the same type, the condition for determining that it is a vector is "the length is 2, and both the first and second elements are real numbers". This determination will be used again later, so make it a function.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos
from numbers import Real #Add import
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Additional part from here#
def __add__(self, other):
#Implementation of addition
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x + v.x, self.y + v.y))
else:
return NotImplemented
#The so-called reflective version of addition
__radd__ = __add__
def __sub__(self, other):
#Implementation of subtraction
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x - v.x, self.y - v.y))
else:
return NotImplemented
def __rsub__(self, other):
#Reflective version of subtraction
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((v.x - self.x, v.y - self.y))
else:
return NotImplemented
@staticmethod
def isvector(obj):
#Function to determine if it is a vector
try:
return (
isinstance(obj[1], Real)
and isinstance(obj[0], Real)
and len(obj) == 2)
except:
return False
#Additional part so far#
@classmethod
def from_coordinates(cls, p1, p2=None):
if p2 is None:
x, y = p1
else:
x = p2[0] - p1[0]
y = p2[1] - p1[1]
r = sqrt(x**2 + y**2)
if r == 0:
return cls(0, 0)
theta = 180 * acos(x / r) / pi
if y < 0:
theta = 360 - theta
return cls(r, theta)
@property
def x(self):
theta = pi * self.direction / 180
return self.length * cos(theta)
@property
def y(self):
theta = pi * self.direction / 180
return self.length * sin(theta)
if __name__ == '__main__':
vec1 = MyVector.from_coordinates((1, 1))
vec2 = MyVector.from_coordinates((-1, -1))
print(vec1 + vec2) #MyVector(length=0, direction=0)
print(vec1 - vec2) #MyVector(length=2.8284...[2√2], direction=45)
The Real
class in the built-in numbers module is a real base class. ʻIsinstance (1024, Real), ʻisinstance (1.024, Real)
, etc. all return True
.
We set __radd__ = __add__
because vector + x
and x + vector
are equivalent. When translated like javascript, it looks like this.__radd__ = this .__ add__
.
Execution result:
MyVector(length=4.965068306494546e-16, direction=153.43494882292202)
MyVector(length=2.8284271247461903, direction=45.00000000000001)
I don't know if it fits. Round it with the round
function to make it easier to see.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos
from numbers import Real
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
def __add__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x + v.x, self.y + v.y))
else:
return NotImplemented
__radd__ = __add__
def __sub__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x - v.x, self.y - v.y))
else:
return NotImplemented
def __rsub__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((v.x - self.x, v.y - self.y))
else:
return NotImplemented
@classmethod
def from_coordinates(cls, p1, p2=None):
if p2 is None:
x, y = p1
else:
x = p2[0] - p1[0]
y = p2[1] - p1[1]
#here
r = round(sqrt(x ** 2 + y ** 2), 3)
if r == 0:
return cls(0, 0)
#here
theta = round(180 * acos(x / r) / pi)
if y < 0:
theta = 360 - theta
return cls(r, theta)
@property
def x(self):
theta = pi * self.direction / 180
#here
return round(self.length * cos(theta), 3)
@property
def y(self):
theta = pi * self.direction / 180
#here
return round(self.length * sin(theta), 3)
if __name__ == '__main__':
vec1 = MyVector.from_coordinates((1, 1))
vec2 = MyVector.from_coordinates((-1, -1))
print(vec1 + vec2) #MyVector(length=0, direction=0)
print(vec1 - vec2) #MyVector(length=2.828, direction=45)
Execution result:
MyVector(length=0, direction=0)
MyVector(length=2.828, direction=45)
it is a good feeling. By the way, the direction
of the result of addition is changed from153.43 ...
to 0
, because it was caught in ʻif r == 0:in
from_coordinates`. As expected. For the time being
+
and -
.Was implemented. The remaining requirements are
*
*
, the size of the vector is multiplied by that real number./
, the size of the vector divided by the real number is returned.is. Next we will implement multiplication.
Multiplication is a requirement that the behavior changes depending on the calculation target. For real numbers and for vectors. You can use the Real
class used earlier to determine the real number. The ʻis vector` function is useful for determining vectors.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos
from numbers import Real
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
def __add__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x + v.x, self.y + v.y))
else:
return NotImplemented
__radd__ = __add__
def __sub__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x - v.x, self.y - v.y))
else:
return NotImplemented
def __rsub__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((v.x - self.x, v.y - self.y))
else:
return NotImplemented
#Additional part from here#
def __mul__(self, other):
cls = self.__class__
if isinstance(other, Real):
#For real numbers
l = round(self.length * other, 3)
return cls(l, self.direction)
elif cls.isvector(other):
#For vector
theta = pi * (other[1] - self[1]) / 180
product = self[0] * other[0] * cos(theta)
return round(product, 3)
else:
#Otherwise
return NotImplemented
__rmul__ = __mul__
#Additional part so far#
@staticmethod
def isvector(obj):
try:
return (
isinstance(obj[1], Real)
and isinstance(obj[0], Real)
and len(obj) == 2)
except:
return False
@classmethod
def from_coordinates(cls, p1, p2=None):
if p2 is None:
x, y = p1
else:
x = p2[0] - p1[0]
y = p2[1] - p1[1]
r = round(sqrt(x**2 + y**2), 3)
if r == 0:
return cls(0, 0)
theta = round(180 * acos(x / r) / pi)
if y < 0:
theta = 360 - theta
return cls(r, theta)
@property
def x(self):
theta = pi * self.direction / 180
return round(self.length * cos(theta), 3)
@property
def y(self):
theta = pi * self.direction / 180
return round(self.length * sin(theta), 3)
if __name__ == '__main__':
vec1 = MyVector(2, 45)
vec2 = MyVector(2, 135)
print(vec1 * 2) #MyVector(length=4, direction=45)
print(vec1 * vec2) #0
Execution result:
MyVector(length=4, direction=45)
0.0
with this,
*
*
, the size of the vector is multiplied by that real number.Was implemented. The remaining requirements are
/
, the size of the vector divided by the real number is returned.is. One more!
We will implement it in the same way as multiplication. As I wrote, I noticed that multiplication and division of real numbers and vectors is a vector with vector1 / vector2 = vector1 * (1 / vector2)
if you think that the size of the real number and the vector are multiplied and divided. It seems that division between each other can also be implemented. I will add that as well.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos
from numbers import Real
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
def __add__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x + v.x, self.y + v.y))
else:
return NotImplemented
__radd__ = __add__
def __sub__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((self.x - v.x, self.y - v.y))
else:
return NotImplemented
def __rsub__(self, other):
if self.isvector(other):
cls = self.__class__
v = cls(*other)
return cls.from_coordinates((v.x - self.x, v.y - self.y))
else:
return NotImplemented
def __mul__(self, other):
cls = self.__class__
if isinstance(other, Real):
l = round(self.length * other, 3)
return cls(l, self.direction)
elif cls.isvector(other):
theta = pi * (other[1] - self[1]) / 180
product = self[0] * other[0] * cos(theta)
return round(product, 3)
else:
return NotImplemented
__rmul__ = __mul__
#Additional part from here#
def __truediv__(self, other):
cls = self.__class__
if isinstance(other, Real):
l = round(self.length / other, 3)
return cls(l, self.direction)
elif cls.isvector(other):
return self * (1 / cls(*other))
else:
return NotImplemented
#Reflective version of division
def __rtruediv__(self, other):
cls = self.__class__
if isinstance(other, Real):
l = round(other / self.length, 3)
return cls(l, self.direction)
elif cls.isvector(other):
return other * (1 / self)
else:
return NotImplemented
#Additional part so far#
@staticmethod
def isvector(obj):
try:
return (
isinstance(obj[1], Real)
and isinstance(obj[0], Real)
and len(obj) == 2)
except:
return False
@classmethod
def from_coordinates(cls, p1, p2=None):
if p2 is None:
x, y = p1
else:
x = p2[0] - p1[0]
y = p2[1] - p1[1]
r = round(sqrt(x**2 + y**2), 3)
if r == 0:
return cls(0, 0)
theta = round(180 * acos(x / r) / pi)
if y < 0:
theta = 360 - theta
return cls(r, theta)
@property
def x(self):
theta = pi * self.direction / 180
return round(self.length * cos(theta), 3)
@property
def y(self):
theta = pi * self.direction / 180
return round(self.length * sin(theta), 3)
if __name__ == '__main__':
vec1 = MyVector(2, 45)
print(vec1 / 2) #MyVector(length=1, direction=45)
print(vec1 / vec1) #1
Execution result:
MyVector(length=1.0, direction=45)
1.0
It went well. Now all the requirements are implemented!
>>> vec1 = MyVector(2, 45)
>>> vec2 = MyVector(2, 135)
>>> vec3 = MyVector(2, 225)
>>> vec1.direction = 256
Traceback (most recent call last):
File "<string>", line 1, in <module>
AttributeError: can`t set attribute
>>> vec1 + vec3
MyVector(length=0, direction=0)
>>> vec1 - vec3
MyVector(length=3.999, direction=45)
>>> vec1 * vec2
0.0
>>> vec1 * (2, 135)
0.0
>>> vec1 * 2
MyVector(length=4, direction=45)
>>> vec1 / 2
MyVector(length=1.0, direction=45)
>>> vec1 += vec3
>>> vec1
MyVector(length=0, direction=0)
Only the result of vec1 --vec3
is a little strange, but roughly as intended.
I created an immutable vector type in pure python. It's not strange here, if you don't understand here, please tell me anything. The value deviation was noticeable on the way, but if you are interested, try using here (decimal built-in module) etc. How is it?
vec2.py
from math import pi, sqrt, acos
from numbers import Real
from operator import itemgetter
class MyVector2(tuple):
_ROUND_DIGIT = 2
__slots__ = ()
def __new__(cls, x=0, y=0):
return tuple.__new__(cls, (x, y))
def __repr__(self):
t = (self.__class__.__name__, *self)
return ('{0}(x={1}, y={2})'.format(*t))
__bool__ = (lambda self: bool(self.length))
def __add__(self, other):
cls = self.__class__
if cls.isvector(other):
return cls(self[0] + other[0], self[1] + other[1])
else:
return NotImplemented
def __sub__(self, other):
cls = self.__class__
if cls.isvector(other):
return cls(self[0] - other[0], self[1] - other[1])
else:
return NotImplemented
def __mul__(self, other):
cls = self.__class__
if cls.isvector(other):
return self[0]*other[0] + self[1]*other[1]
elif isinstance(other, Real):
return cls(self.x * other, self.y * other)
else:
return NotImplemented
def __truediv__(self, other):
cls = self.__class__
if cls.isvector(other):
v = cls(*other)
return self * (1 / v)
elif isinstance(other, Real):
return cls(self.x / other, self.y / other)
else:
return NotImplemented
__radd__ = __add__
__rmul__ = __mul__
def __rsub__(self, other):
return -(self - other)
def __rtruediv__(self, other):
cls = self.__class__
if isinstance(other, Real):
return cls(other / self.x, other / self.y)
elif cls.isvector(other):
return other * (1 / self)
else:
return NotImplemented
__abs__ = (lambda self: abs(self.length))
__pos__ = (lambda self: self)
def __neg__(self):
x, y = -self.x, -self.y
return self.__class__(x, y)
def __complex__(self):
return self.x + self.y * 1j
@staticmethod
def isvector(obj):
try:
return (
isinstance(obj[1], Real)
and isinstance(obj[0], Real)
and len(obj) == 2)
except:
return False
@classmethod
def _round(cls, a):
return round(a, cls._ROUND_DIGIT)
x = property(itemgetter(0))
y = property(itemgetter(1))
@property
def length(self):
r = sqrt(self.x**2 + self.y**2)
return self._round(r)
@property
def direction(self):
r = self.length
if r == 0:
return 0
x, y = self
theta = self._round(180 * acos(x / r) / pi)
if y < 0:
theta = 360 - theta
return theta
I will try using it.
>>> v1 = MyVector2(1, 1)
>>> v2 = MyVector2(-1, -1)
>>> v1 + v2
MyVector2(x=0, y=0)
>>> v1 - v2
MyVector2(x=2, y=2)
>>> v1 * v2
-2
>>> v1 / v2
-2.0
>>> v1.direction
44.83
>>> v1.length
1.41
>>> v2.direction
224.83
>>> v1 / (-1, -1)
-2.0
>>> v1 + (1, 1)
MyVector2(x=2, y=2)
>>> (1, 1) + v1
MyVector2(x=2, y=2)
>>> v1 - (1, 1)
MyVector2(x=0, y=0)
>>> (1, 1) - v1
MyVector2(x=0, y=0)
>>> v1 * 2
MyVector2(x=2, y=2)
>>> v1 / 2
MyVector2(x=0.5, y=0.5)
vector.py
from operator import itemgetter
from math import pi, cos, sin, sqrt, acos
from numbers import Real
import abc
def isvector(obj):
try:
return (
isinstance(obj[0], Real)
and isinstance(obj[1], Real)
and len(obj) == 2)
except:
return False
def empty(*args, **kwargs): pass
class MyABCMeta(abc.ABCMeta):
def __new__(cls, name, bases=(), namespace={}, ab_attrs=(), **kwargs):
namespace.update(cls.__prepare__(name, bases, ab_attrs))
return super().__new__(cls, name, bases, namespace, **kwargs)
@classmethod
def __prepare__(cls, name, bases, ab_attrs=()):
result = {
name: abc.abstractmethod(empty) \
for name in ab_attrs
}
return result
class ABVector(
tuple,
metaclass=MyABCMeta,
ab_attrs=(
'x', 'y', 'length', 'direction', #properties
'from_xy', 'from_ld', #converter [argments: (cls, vector: tuple)]
'__repr__', '__bool__', '__abs__',
'__pos__', '__neg__', '__complex__',
'__eq__', '__add__', '__radd__',
'__sub__', '__rsub__','__mul__',
'__rmul__', '__truediv__', '__rtruediv__'
)
):
__slots__ = ()
ROUND_DIGIT = 3
@classmethod
def round(cls, a):
return round(a, cls.ROUND_DIGIT)
@classmethod
def __instancecheck__(cls, instance):
return all(map(
(lambda string: hasattr(instance, string)),
('x', 'y', 'length', 'direction')
))
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
class XYVector(ABVector):
_fields_ = ('x', 'y')
def __new__(cls, x=0, y=0):
t = (cls.round(x), cls.round(y))
return super().__new__(cls, t)
def __repr__(self):
t = (self.__class__.__name__, *self)
return f'{self.__class__.__name__}(x={self.x}, y={self.y})'
def __bool__(self):
return bool(self.length)
def __abs__(self):
return abs(self.length)
__pos__ = lambda self: self
def __neg__(self):
t = (-self.x, -self.y)
return self.__class__(*t)
def __complex__(self):
return self.x + self.y * 1j
def __eq__(self, other):
try:
return (
self.x == other.x
and self.y == other.y
)
except:
return False
# operation implementation: start #
template = """
def __{name}__(self, other):
cls = self.__class__
if isinstance(other, ABVector):
x = {0} self.x {1} other.x
y = {0} self.y {1} other.y
return cls(x, y)
elif isvector(other):
vec = cls(*other)
x = {0} self.x {1} vec.x
y = {0} self.y {1} vec.y
return cls(x, y)
else:
return NotImplemented
""".replace(f'\n{" " * 4}', '\n')
order = (
(('+', '+'), {'name': 'add'}),
(('+', '+'), {'name': 'radd'}),
(('+', '-'), {'name': 'sub'}),
(('-', '+'), {'name': 'rsub'}),
)
for t, d in order:
exec(template.format(*t, **d))
template = """
def __{name}__(self, other):
cls = self.__class__
if isinstance(other, Real):
x = cls.round({0} self.x {1} other)
y = cls.round({0} self.y {1} other)
return cls(x, y)
elif isinstance(other, ABVector):
product = (self.x * other.x + self.y * other.y) / {2}
return cls.round(product)
elif isvector(other):
other = cls(*other)
product = (self.x * other.x + self.y * other.y) / {2}
return cls.round(product)
else:
return NotImplemented
""".replace(f'\n{" " * 4}', '\n')
order = (
(('', '*', 1), {'name': 'mul'}),
(('', '*', 1), {'name': 'rmul'}),
(('', '/', '(other.x ** 2 + other.y ** 2)'), {'name': 'truediv'}),
(('1 /', '*', '(self.x ** 2 + self.y ** 2)'), {'name': 'rtruediv'}),
)
for t, d in order:
exec(template.format(*t, **d))
del template, order, t, d
# operation implementation: end #
@classmethod
def from_xy(cls, vector):
return cls(*vector)
@classmethod
def from_ld(cls, vector):
l, d = vector
t = pi * d / 180
x = l * cos(t)
y = l * sin(t)
return cls(x, y)
x = property(itemgetter(0))
y = property(itemgetter(1))
@property
def length(self):
r = sqrt(self.x ** 2 + self.y ** 2)
return self.__class__.round(r)
@property
def direction(self):
r = self.length
if r == 0:
return 0
x, y = self
theta = round(180 * acos(x / r) / pi)
if y < 0:
theta = 360 - theta
return theta
class LDVector(ABVector):
_fields_ = ('length', 'direction')
def __new__(cls, length=0, direction=0):
t = (cls.round(length), round(direction))
return super().__new__(cls, t)
def __repr__(self):
return f'{self.__class__.__name__}(length={self.length}, direction={self.direction})'
def __bool__(self):
return bool(self.length)
def __abs__(self):
return abs(self.length)
__pos__ = lambda self: self
def __neg__(self):
d = self.direction + 180
if d <= 360:
d -= 360
return self.__class__(self.length, d)
def __complex__(self):
return self.x + self.y * 1j
def __eq__(self, other):
try:
return (
self.length == other.length
and self.direction == other.direction
)
except:
return False
# operation implementation: start #
template = """
def __{name}__(self, other):
cls = self.__class__
if isinstance(other, ABVector):
x = {0} self.x {1} other.x
y = {0} self.y {1} other.y
return cls.from_xy((x, y))
elif isvector(other):
vec = cls(*other)
x = {0} self.x {1} vec.x
y = {0} self.y {1} vec.y
return cls.from_xy((x, y))
else:
return NotImplemented
""".replace(f'\n{" " * 4}', '\n')
order = (
(('+', '+'), {'name': 'add'}),
(('+', '+'), {'name': 'radd'}),
(('+', '-'), {'name': 'sub'}),
(('-', '+'), {'name': 'rsub'}),
)
for t, d in order:
exec(template.format(*t, **d))
template = """
def __{name}__(self, other):
cls = self.__class__
if isinstance(other, Real):
l = cls.round({0} self.length {1} other)
return cls(l, self.direction)
elif isinstance(other, ABVector):
t = pi * (self.direction - other.direction) / 180
product = (self.length * other.length * cos(t)) / {2}
return cls.round(product)
elif isvector(other):
other = cls(*other)
t = pi * (self.direction - other.direction) / 180
product = (self.length * other.length * cos(t)) / {2}
return cls.round(product)
else:
return NotImplemented
""".replace(f'\n{" " * 4}', '\n')
order = (
(('1 *', '*', '1'), {'name': 'mul'}),
(('1 *', '*', '1'), {'name': 'rmul'}),
(('1 *', '/', '(other.length ** 2)'), {'name': 'truediv'}),
(('1 /', '*', '(self.length ** 2)'), {'name': 'rtruediv'}),
)
for t, d in order:
exec(template.format(*t, **d))
del template, order, t, d
# operation implementation: end #
@classmethod
def from_ld(cls, vector):
return cls(*vector)
@classmethod
def from_xy(cls, vector):
x, y = vector
l = sqrt(x ** 2 + y ** 2)
if l == 0:
return cls(0, 0)
d = round(180 * acos(x / l) / pi)
if y < 0:
d = 360 - d
return cls(l, d)
length = property(itemgetter(0))
direction = property(itemgetter(1))
@property
def x(self):
d = pi * self.direction / 180
return self.__class__.round(self.length * cos(d))
@property
def y(self):
d = pi * self.direction / 180
return self.__class__.round(self.length * sin(d))
XYVector.to_ld = (lambda self: LDVector.from_xy(self))
XYVector.to_xy = (lambda self: XYVector.from_xy(self))
LDVector.to_xy = (lambda self: XYVector.from_ld(self))
LDVector.to_ld = (lambda self: LDVector.from_ld(self))
if __name__ == "__main__":
xyv1 = XYVector(1, 1)
xyv2 = XYVector(-1, 1)
ldv1 = LDVector.from_xy((-1, -1))
ldv2 = LDVector.from_xy((1, -1))
codes = ('xyv1', 'xyv2', 'ldv1', 'ldv2')
tmpls = (
'xyv1 {0} xyv2', 'xyv1 {0} ldv1', 'xyv1 {0} ldv2',
'xyv2 {0} xyv1', 'xyv2 {0} ldv1', 'xyv2 {0} ldv2',
'ldv1 {0} xyv1', 'ldv1 {0} xyv2', 'ldv1 {0} ldv2',
'ldv2 {0} xyv1', 'ldv2 {0} xyv2', 'ldv2 {0} ldv1',
)
order = tuple('+-*/')
for e in order:
codes += tuple(map(
(lambda tmpl: tmpl.format(*e)),
tmpls
))
for code in codes:
print(f'{code} = {eval(code)}')
Execution result:
xyv1 = XYVector(x=1, y=1)
xyv2 = XYVector(x=-1, y=1)
ldv1 = LDVector(length=1.414, direction=225)
ldv2 = LDVector(length=1.414, direction=315)
xyv1 + xyv2 = XYVector(x=0, y=2)
xyv1 + ldv1 = XYVector(x=0.0, y=0.0)
xyv1 + ldv2 = XYVector(x=2.0, y=0.0)
xyv2 + xyv1 = XYVector(x=0, y=2)
xyv2 + ldv1 = XYVector(x=-2.0, y=0.0)
xyv2 + ldv2 = XYVector(x=0.0, y=0.0)
ldv1 + xyv1 = LDVector(length=0, direction=0)
ldv1 + xyv2 = LDVector(length=2.0, direction=180)
ldv1 + ldv2 = LDVector(length=2.0, direction=270)
ldv2 + xyv1 = LDVector(length=2.0, direction=0)
ldv2 + xyv2 = LDVector(length=0, direction=0)
ldv2 + ldv1 = LDVector(length=2.0, direction=270)
xyv1 - xyv2 = XYVector(x=2, y=0)
xyv1 - ldv1 = XYVector(x=2.0, y=2.0)
xyv1 - ldv2 = XYVector(x=0.0, y=2.0)
xyv2 - xyv1 = XYVector(x=-2, y=0)
xyv2 - ldv1 = XYVector(x=0.0, y=2.0)
xyv2 - ldv2 = XYVector(x=-2.0, y=2.0)
ldv1 - xyv1 = LDVector(length=2.828, direction=225)
ldv1 - xyv2 = LDVector(length=2.0, direction=270)
ldv1 - ldv2 = LDVector(length=2.0, direction=180)
ldv2 - xyv1 = LDVector(length=2.0, direction=270)
ldv2 - xyv2 = LDVector(length=2.828, direction=315)
ldv2 - ldv1 = LDVector(length=2.0, direction=0)
xyv1 * xyv2 = 0.0
xyv1 * ldv1 = -2.0
xyv1 * ldv2 = 0.0
xyv2 * xyv1 = 0.0
xyv2 * ldv1 = 0.0
xyv2 * ldv2 = -2.0
ldv1 * xyv1 = -1.999
ldv1 * xyv2 = 0.0
ldv1 * ldv2 = 0.0
ldv2 * xyv1 = -0.0
ldv2 * xyv2 = -1.999
ldv2 * ldv1 = 0.0
xyv1 / xyv2 = 0.0
xyv1 / ldv1 = -1.0
xyv1 / ldv2 = 0.0
xyv2 / xyv1 = 0.0
xyv2 / ldv1 = 0.0
xyv2 / ldv2 = -1.0
ldv1 / xyv1 = -1.0
ldv1 / xyv2 = 0.0
ldv1 / ldv2 = 0.0
ldv2 / xyv1 = -0.0
ldv2 / xyv2 = -1.0
ldv2 / ldv1 = 0.0
Recommended Posts