C'est H1rono.
J'ai créé un type immuable (immuable) avec du python pur. J'espère pouvoir vous dire en détail ce que j'ai fait.
Créez un type de vecteur. Plus précisément,
MyVector
Mon vecteur (longueur = taille, direction = direction)
dans l'interpréteurpropriété
nommée x
, y
(par exemple, vecteur.x`)+
et -
*
/
, la taille du vecteur divisée par le nombre réel est renvoyée.Créez une classe avec de telles exigences.
Tout d'abord, mettez en œuvre l'exigence «immuable».
Citation immuable du Glossaire officiel
immutable (Immuable) Un objet avec une valeur fixe. Les objets immuables incluent les nombres, les chaînes et les taples. Les valeurs de ces objets ne peuvent pas être modifiées. Si vous voulez vous souvenir d'une autre valeur, vous devez créer un nouvel objet. Les objets immuables jouent un rôle important dans les situations où des valeurs de hachage fixes sont requises. Une clé de dictionnaire est un exemple.
En d'autres termes, il semble que vous ne puissiez pas dire «vector.direction = 256».
Ce qui est génial, c'est le tuple nommé dans le module intégré collections. Avec la fonction 3 / library / collections.html # collections.namedtuple), vous pouvez facilement créer une classe avec un nom attribué à cet élément dans une sous-classe de tuple
(c'est-à-dire immuable). python est incroyable.
vec.py
from collections import namedtuple
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
pass #Répondez aux autres exigences ici
if __name__ == '__main__':
vec = MyVector(4, 2)
print(vec) #MyVector(length=4, direction=2)
Résultat de l'exécution:
MyVector(length=4, direction=2)
Maintenant, parmi les exigences mentionnées ci-dessus,
MyVector
Mon vecteur (longueur = taille, direction = direction)
dans l'interpréteurA pu être mis en œuvre pour le moment. Les autres exigences sont
propriété
nommée x
, y
+
et -
*
/
, la taille du vecteur divisée par le nombre réel est renvoyée.est. Il a diminué d'un seul coup.
Ce qui suit implémente l'exigence selon laquelle «les coordonnées en tant que vecteur de position peuvent être référencées par une propriété nommée« x »,« y »». Utilisez le module intégré math. L'expression de «x» est «length * cos (pi * direction / 180)», et l'expression de «y» est «length * sin (pi * direction / 180)».
vec.py
from collections import namedtuple
from math import pi, cos, sin #Ajouter une importation
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Partie supplémentaire d'ici#
@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)
#Partie supplémentaire jusqu'à présent#
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
Comme vous pouvez le voir en exécutant le code ci-dessus, le résultat de l'exécution n'est pas aussi commenté. Dans mon environnement,
x = 1.0000000000000002
y = 1.0
Il sera affiché. Ce comportement est probablement lié à ici (l'arithmétique en virgule flottante, ses problèmes et ses limitations). pense. Si vous êtes intéressé, s'il vous plaît. Bref, pour le moment
propriété
nommée x
, y
A pu être mis en œuvre. Le reste,
+
et -
*
/
, la taille du vecteur divisée par le nombre réel est renvoyée.est. Tout est une implémentation arithmétique.
Référence: Référence officielle (émule les types numériques)
La correspondance entre les opérateurs et les noms de méthode implémentés cette fois est à peu près comme indiqué dans le tableau suivant.
opérateur | Nom de la méthode |
---|---|
+ |
__add__ , __radd__ |
- |
__sub__ , __rsub__ |
* |
__mul__ , __rmul__ |
/ |
__truediv__ , __rtruediv__ |
Toutes les fonctions ont des arguments (self, other)
. En d'autres termes, lorsque vector = Myvector (8, 128)
, vector + x
appelle vector .__ add__ (x)
et renvoie le résultat comme résultat du calcul.
Les méthodes avec r
au début, telles que __radd__
, implémentent" ce que l'opérateur est reflété (remplacé) "(cité à partir de l'URL ci-dessus). Autrement dit, comme «x + vector», lorsque «x .__ add__ (vector)« retourne »Non implémenté», le résultat de «vector .__ radd __ (x)» est renvoyé. Comme c'est un gros problème, j'ajouterai celui-ci également.
La méthode de mise en œuvre est comme ça. Écrivons-le.
Je voudrais dire que, dans la mise en œuvre de l'addition et de la soustraction, il est nécessaire d'obtenir le vecteur représenté par (longueur, direction)
à partir des coordonnées de (x, y)
. Tout d'abord, implémentez cette méthode.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos #Ajouter une importation
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Partie supplémentaire d'ici#
@classmethod
def from_coordinates(cls, p1, p2=None):
#p1: (x, y)
#p2: (x, y)Ou aucun
if p2 is None:
#Traiter comme un vecteur de position
x, y = p1
else:
#À partir de p1,Traitez p2 comme un vecteur de points finaux
x = p2[0] - p1[0]
y = p2[1] - p1[1]
r = sqrt(x**2 + y**2)
if r == 0:
#0 vecteur
return cls(0, 0)
#Fonction de triangle inversé
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)
#Partie supplémentaire jusqu'à présent#
@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
Ajout de la fonction from_coordinates
. Comme son nom l'indique, il crée un objet MyVector
à partir des coordonnées. Je l'ai choisi comme méthode de classe car je l'ai trouvé très pratique. Résultat de l'exécution:
x = 1.9999999999999996
y = 2.0000000000000004
A plus tard ... Vous pouvez ajouter la fonction round
et l'arrondir plus tard. Quoi qu'il en soit, j'ai pu obtenir le vecteur représenté par (longueur, direction)
à partir des coordonnées de (x, y)
.
Ensuite, nous implémenterons l'addition et la soustraction. Puisque la cible de calcul prise en charge est un vecteur du même type, la condition pour déterminer qu'il s'agit d'un vecteur est "la longueur est de 2, et les premier et second éléments sont des nombres réels". Cette détermination sera réutilisée plus tard, alors faites-en une fonction.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos
from numbers import Real #Ajouter une importation
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Partie supplémentaire d'ici#
def __add__(self, other):
#Mise en œuvre de l'ajout
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
#La version dite réfléchissante de l'addition
__radd__ = __add__
def __sub__(self, other):
#Mise en œuvre de la soustraction
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):
#Version réfléchissante de la soustraction
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):
#Fonction pour déterminer s'il s'agit d'un vecteur
try:
return (
isinstance(obj[1], Real)
and isinstance(obj[0], Real)
and len(obj) == 2)
except:
return False
#Partie supplémentaire jusqu'à présent#
@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)
La classe Real
du module intégré numbers est une vraie classe de base. ʻIsinstance (1024, Real) , ʻisinstance (1.024, Real)
, etc. retournent tous True
.
Nous définissons «radd = add» car «vector + x» et «x + vector» ont la même valeur. Traduit comme javascript, il ressemble à this .__ radd__ = this .__ add__
.
Résultat de l'exécution:
MyVector(length=4.965068306494546e-16, direction=153.43494882292202)
MyVector(length=2.8284271247461903, direction=45.00000000000001)
Je ne sais pas si ça va. Utilisez la fonction «round» pour l'arrondir pour une meilleure visualisation.
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]
#ici
r = round(sqrt(x ** 2 + y ** 2), 3)
if r == 0:
return cls(0, 0)
#ici
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
#ici
return round(self.length * cos(theta), 3)
@property
def y(self):
theta = pi * self.direction / 180
#ici
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)
Résultat de l'exécution:
MyVector(length=0, direction=0)
MyVector(length=2.828, direction=45)
C'est un sentiment agréable. Au fait, la "direction" du résultat de l'addition passe de "153.43 ..." à "0", car elle a été interceptée dans "if r == 0:" in "from_coordinates". Comme prévu. Pour le moment
+
et -
A pu être mis en œuvre. Les autres exigences sont
*
/
, la taille du vecteur divisée par le nombre réel est renvoyée.est. Ensuite, nous mettrons en œuvre la multiplication.
La multiplication est une exigence que le comportement change en fonction de la cible de calcul. Pour les nombres réels et pour les vecteurs. Vous pouvez utiliser la classe Real
utilisée précédemment pour déterminer le nombre réel. La fonction «est un vecteur» est utile pour déterminer les vecteurs.
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
#Partie supplémentaire d'ici#
def __mul__(self, other):
cls = self.__class__
if isinstance(other, Real):
#Pour des nombres réels
l = round(self.length * other, 3)
return cls(l, self.direction)
elif cls.isvector(other):
#Pour le vecteur
theta = pi * (other[1] - self[1]) / 180
product = self[0] * other[0] * cos(theta)
return round(product, 3)
else:
#Autrement
return NotImplemented
__rmul__ = __mul__
#Partie supplémentaire jusqu'à présent#
@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
Résultat de l'exécution:
MyVector(length=4, direction=45)
0.0
avec ça,
*
A pu être mis en œuvre. Les autres exigences sont
/
, la taille du vecteur divisée par le nombre réel est renvoyée.est. Un de plus!
Nous le mettrons en œuvre de la même manière que la multiplication. Comme je l'ai écrit, j'ai remarqué qu'en pensant que le nombre réel et la magnitude du vecteur sont multipliés et divisés dans la multiplication et la division du nombre réel et du vecteur, le vecteur devient vecteur1 / vecteur2 = vecteur1 * (1 / vecteur2)
. Il semble que vous puissiez également mettre en œuvre une division entre eux. J'ajouterai cela aussi.
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__
#Partie supplémentaire d'ici#
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
#Version réfléchissante de la 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
#Partie supplémentaire jusqu'à présent#
@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
Résultat de l'exécution:
MyVector(length=1.0, direction=45)
1.0
Ça s'est bien passé. Maintenant, toutes les exigences sont implémentées!
>>> 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)
Seul le résultat de vec1 --vec3
est un peu étrange, mais à peu près comme prévu.
J'ai créé un type de vecteur immuable en python pur. Ce n'est pas étrange ici, si vous ne comprenez pas ici, dites-moi quelque chose. L'écart de valeur était perceptible en cours de route, mais si vous êtes intéressé, essayez d'utiliser here (module intégré décimal) etc. Comment c'est?
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
J'essaierai de l'utiliser.
>>> 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)}')
Résultat de l'exécution:
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