Das ist H1rono.
Ich habe einen unveränderlichen Typ mit reinem Python gemacht. Ich hoffe, ich kann Ihnen ausführlich sagen, was ich getan habe.
Machen Sie einen Vektortyp. Speziell,
+
und -
addieren oder subtrahieren*
multipliziert werden*
multipliziert wird, wird die Größe des Vektors mit dieser reellen Zahl multipliziert./
geteilt werden, wird die Größe des Vektors geteilt durch die reelle Zahl zurückgegeben.Erstellen Sie eine Klasse mit solchen Anforderungen.
Implementieren Sie zunächst die Anforderung "unveränderlich".
Unveränderliches Zitat aus Offizielles Glossar
immutable (Unveränderlich) Ein Objekt mit einem festen Wert. Unveränderliche Objekte sind Zahlen, Zeichenfolgen und Taples. Die Werte dieser Objekte können nicht geändert werden. Wenn Sie sich einen anderen Wert merken möchten, müssen Sie ein neues Objekt erstellen. Unveränderliche Objekte spielen eine wichtige Rolle in Situationen, in denen feste Hashwerte erforderlich sind. Ein Wörterbuchschlüssel ist ein Beispiel.
Mit anderen Worten, es scheint, dass Sie nicht "vector.direction = 256" sagen können.
Was großartig ist, ist das benannte Tupel in den integrierten Sammlungen Modul. Mit der Funktion 3 / library / collection.html # collection.namedtuple) können Sie einfach eine Unterklasse von "Tupel" (dh unveränderlich) mit einem diesem Element zugewiesenen Namen erstellen. Python ist unglaublich.
vec.py
from collections import namedtuple
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
pass #Erfüllen Sie hier andere Anforderungen
if __name__ == '__main__':
vec = MyVector(4, 2)
print(vec) #MyVector(length=4, direction=2)
Ausführungsergebnis:
MyVector(length=4, direction=2)
Nun, unter den oben genannten Anforderungen,
Konnte vorerst umgesetzt werden. Die restlichen Anforderungen sind
+
und -
addieren oder subtrahieren*
multipliziert werden*
multipliziert wird, wird die Größe des Vektors mit dieser reellen Zahl multipliziert./
geteilt werden, wird die Größe des Vektors geteilt durch die reelle Zahl zurückgegeben.ist. Es hat auf einmal abgenommen.
Das Folgende implementiert die Anforderung, dass "Koordinaten als Positionsvektor durch" Eigenschaft "mit den Namen" x "," y "referenziert werden können". Verwenden Sie das integrierte Modul math. Der Ausdruck für "x" ist "Länge * cos (pi * Richtung / 180)", und der Ausdruck für "y" ist "Länge * sin (pi * Richtung / 180)".
vec.py
from collections import namedtuple
from math import pi, cos, sin #Import hinzufügen
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Zusätzlicher Teil von hier#
@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)
#Zusätzlicher Teil bisher#
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
Wie Sie durch Ausführen des obigen Codes sehen können, ist das Ausführungsergebnis nicht so kommentiert. In meiner Umgebung
x = 1.0000000000000002
y = 1.0
Es wird angezeigt. Dieses Verhalten hängt wahrscheinlich mit hier (Gleitkomma-Arithmetik, ihre Probleme und Einschränkungen) zusammen. Überlegen. Wenn Sie interessiert sind, bitte. Jedenfalls vorerst
Konnte umgesetzt werden. Der Rest,
+
und -
addieren oder subtrahieren*
multipliziert werden*
multipliziert wird, wird die Größe des Vektors mit dieser reellen Zahl multipliziert./
geteilt werden, wird die Größe des Vektors geteilt durch die reelle Zahl zurückgegeben.ist. Es ist alles eine arithmetische Implementierung.
Referenz: Offizielle Referenz (emuliert numerische Typen)
Die diesmal implementierte Entsprechung zwischen den Operatoren und Methodennamen entspricht in etwa der folgenden Tabelle.
Operator | Methodenname |
---|---|
+ |
__add__ , __radd__ |
- |
__sub__ , __rsub__ |
* |
__mul__ , __rmul__ |
/ |
__truediv__ , __rtruediv__ |
Alle Funktionen haben (self, other)
Argumente. Mit anderen Worten, wenn vector = Myvector (8, 128)
, ruftvector + x`` vector .__ add__ (x)
auf und gibt das Ergebnis als Berechnungsergebnis zurück.
Methoden mit "r" am Anfang, wie "radd", implementieren "was der Operator widerspiegelt (ersetzt)" (zitiert aus der obigen URL). Das heißt, als "x + vector" wird das Ergebnis von "vector .__ radd __ (x)" zurückgegeben, wenn "x .__ add__ (vector)" nicht implementiert "zurückgibt. Da es eine große Sache ist, werde ich auch diese hinzufügen.
Die Implementierungsmethode ist wie folgt. Lass es uns schreiben.
Ich möchte sagen, dass es bei der Implementierung von Addition und Subtraktion notwendig ist, den durch "(Länge, Richtung)" dargestellten Vektor aus den Koordinaten von "(x, y)" zu erhalten. Implementieren Sie zunächst diese Methode.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos #Import hinzufügen
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Zusätzlicher Teil von hier#
@classmethod
def from_coordinates(cls, p1, p2=None):
#p1: (x, y)
#p2: (x, y)Oder keine
if p2 is None:
#Als Positionsvektor behandeln
x, y = p1
else:
#Ab p1,Behandle p2 als einen Vektor von Endpunkten
x = p2[0] - p1[0]
y = p2[1] - p1[1]
r = sqrt(x**2 + y**2)
if r == 0:
#0 Vektor
return cls(0, 0)
#Inverse Dreiecksfunktion
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)
#Zusätzlicher Teil bisher#
@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
Die Funktion from_coordinates
wurde hinzugefügt. Wie der Name schon sagt, wird aus den Koordinaten ein "MyVector" -Objekt erstellt. Ich habe die Klassenmethode gewählt, weil ich sie sehr praktisch fand. Ausführungsergebnis:
x = 1.9999999999999996
y = 2.0000000000000004
Bis später ... Sie können die Funktion "Runden" hinzufügen und später runden. Wie auch immer, ich konnte den durch "(Länge, Richtung)" dargestellten Vektor aus den Koordinaten von "(x, y)" erhalten.
Als nächstes werden wir Addition und Subtraktion implementieren. Da das unterstützte Berechnungsziel ein Vektor des gleichen Typs ist, ist die Bedingung für die Bestimmung, dass es sich um einen Vektor handelt, "die Länge ist 2, und sowohl das erste als auch das zweite Element sind reelle Zahlen". Diese Bestimmung wird später erneut verwendet, machen Sie sie also zu einer Funktion.
vec.py
from collections import namedtuple
from math import pi, cos, sin, sqrt, acos
from numbers import Real #Import hinzufügen
_MyVector = namedtuple('_MyVector', ('length', 'direction'))
class MyVector(_MyVector):
#Zusätzlicher Teil von hier#
def __add__(self, other):
#Implementierung der 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
#Die sogenannte reflektierende Version des Zusatzes
__radd__ = __add__
def __sub__(self, other):
#Implementierung der Subtraktion
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):
#Reflektierende Version der Subtraktion
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):
#Funktion zum Bestimmen, ob es sich um einen Vektor handelt
try:
return (
isinstance(obj[1], Real)
and isinstance(obj[0], Real)
and len(obj) == 2)
except:
return False
#Zusätzlicher Teil bisher#
@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)
Die Real
-Klasse des integrierten Moduls numbers ist eine echte Basisklasse. "isinstance (1024, Real)", "isinstance (1.024, Real)" usw. geben alle "True" zurück.
Wir setzen __radd__ = __add__
, weil vector + x
und x + vector
den gleichen Wert haben. Übersetzt wie Javascript sieht es aus wie this .__ radd__ = this .__ add__
.
Ausführungsergebnis:
MyVector(length=4.965068306494546e-16, direction=153.43494882292202)
MyVector(length=2.8284271247461903, direction=45.00000000000001)
Ich weiß nicht, ob es passt. Verwenden Sie die Funktion "Runden", um die Anzeige zu erleichtern.
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]
#Hier
r = round(sqrt(x ** 2 + y ** 2), 3)
if r == 0:
return cls(0, 0)
#Hier
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
#Hier
return round(self.length * cos(theta), 3)
@property
def y(self):
theta = pi * self.direction / 180
#Hier
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)
Ausführungsergebnis:
MyVector(length=0, direction=0)
MyVector(length=2.828, direction=45)
Es ist ein gutes Gefühl. Übrigens wird die "Richtung" des Additionsergebnisses von "153,43 ..." auf "0" geändert, weil es in "if r == 0:" in "from_coordinates" abgefangen wurde. Wie erwartet. Vorerst
+
und -
addieren oder subtrahierenKonnte umgesetzt werden. Die restlichen Anforderungen sind
*
multipliziert werden*
multipliziert wird, wird die Größe des Vektors mit dieser reellen Zahl multipliziert./
geteilt werden, wird die Größe des Vektors geteilt durch die reelle Zahl zurückgegeben.ist. Als nächstes werden wir die Multiplikation implementieren.
Die Multiplikation ist eine Voraussetzung dafür, dass sich das Verhalten je nach Berechnungsziel ändert. Für reelle Zahlen und für Vektoren. Sie können die zuvor verwendete Klasse "Real" verwenden, um die reelle Zahl zu bestimmen. Die Funktion "isvector" ist nützlich zum Bestimmen von Vektoren.
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
#Zusätzlicher Teil von hier#
def __mul__(self, other):
cls = self.__class__
if isinstance(other, Real):
#Für reelle Zahlen
l = round(self.length * other, 3)
return cls(l, self.direction)
elif cls.isvector(other):
#Für Vektor
theta = pi * (other[1] - self[1]) / 180
product = self[0] * other[0] * cos(theta)
return round(product, 3)
else:
#Andernfalls
return NotImplemented
__rmul__ = __mul__
#Zusätzlicher Teil bisher#
@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
Ausführungsergebnis:
MyVector(length=4, direction=45)
0.0
mit diesem,
*
multipliziert werden*
multipliziert wird, wird die Größe des Vektors mit dieser reellen Zahl multipliziert.Konnte umgesetzt werden. Die restlichen Anforderungen sind
/
geteilt werden, wird die Größe des Vektors geteilt durch die reelle Zahl zurückgegeben.ist. Einer noch!
Wir werden es genauso implementieren wie die Multiplikation. Ich habe es beim Schreiben bemerkt, aber wenn Sie denken, dass die reelle Zahl und die Größe des Vektors in der Multiplikation und Division der reellen Zahl und des Vektors multipliziert und dividiert werden, dann "vector1 / vector2 = vector1 * (1 / vector2)" und der Vektor Es scheint, dass Sie auch eine Trennung untereinander implementieren können. Ich werde das auch hinzufügen.
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__
#Zusätzlicher Teil von hier#
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
#Reflektierende Version der Teilung
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
#Zusätzlicher Teil bisher#
@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
Ausführungsergebnis:
MyVector(length=1.0, direction=45)
1.0
Es ging gut. Jetzt sind alle Anforderungen erfüllt!
>>> 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)
Nur das Ergebnis von vec1 --vec3
ist etwas seltsam, aber ungefähr wie beabsichtigt.
Ich habe einen unveränderlichen Vektortyp in reinem Python erstellt. Es ist hier nicht seltsam, wenn Sie hier nicht verstehen, sagen Sie mir bitte etwas. Die Wertabweichung war unterwegs spürbar, aber wenn Sie interessiert sind, versuchen Sie es mit hier (eingebautes Dezimalmodul) usw. Wie ist das?
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
Ich werde es versuchen.
>>> 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)}')
Ausführungsergebnis:
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