Конструктор операций над классом не конструирует
Есть класс двухмерного вектора.
class vec(object):
def __init__(self, x=int(), y=int()):
self.x=x
self.y=y
Требуется объявить методы операций с классом ( vec + vec, -, *, /, //, ...)
...
def __add__(self, vector):
return self.__class__(self.x + vector.x, self.y + vector.y)
...
Все они отличаются только именем метода и двумя одинаковыми символами. От этой мысли я начал думать над конструктором этих методов.
В целом все мои варианты не отличались от этого:
...
def __math__(self, vector, operation):
return eval(f"self.__class__(self.x {operation} vector.x, self.y {operation} vector.y)")
def __init__(self, x=int(), self.y=int()):
...
for attr, operation in (("add", '+'), ):
setattr(self, f"__{attr}__", lambda self, vector: self.__math__(vector, operation)
...
По моему взгляду конструктор должен успешно добавить все методы корректно. Никаких ошибок до операций не возникает, но после оказывается, что конструктор ничерта не конструировал:
>>> vec(5, 5) + vec(2, 15)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'vec' and 'vec'
В чем его проблема? Может знаете как сделать конструктор лучше?
Интересный тест:
class vec(object):
def __math__(self, vector, operation):
return eval(f"self.__class__(self.x {operation} vector.x, self.y {operation} vector.y)")
def __init__(self, x=int(), y=int()):
self.x=x
self.y=y
for name, operation in (("add", '+'), ):
setattr(self, f"__{name}__", lambda self, vector: self.__math__(vector, operation))
if __name__ != "__main__":
try:
vec(100, 500) + vec(900, 500)
except TypeError:
print("Failed\n", dir(vec()), '\n\n')
Импортируйте в терминале библиотеку с этим тестом. Результат:
>>> import muz
Failed
['__add__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__math__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']
>>> muz.vec().__add__(muz.vec(7, 6), muz.vec(5, 2))
<muz.vec object at 0x00E3BB50>
То есть внимание на "Failed": операция сложения vec + vec невозможна. Следом печатаем список атрибутов класса вектора. Внимание на первых элемент списка аттрибутов: __add__ имеется. Казалось бы, в чем твоя проблема, блин? Вызвать __add__ мне удалось только каким то ломаным способом (вторая строка снизу).
Ответы (1 шт):
Методы объекта необходимо создавать на этапе создания объекта (метод __new__). В __init__ же происходит уже инициализация.
from itertools import starmap
class Vector:
@classmethod
def method_factory(cls, name):
assert hasattr(int, name), f'{name} not a method int'
def method(a, b):
return cls(*starmap(lambda x, y: getattr(x, name)(y), zip(a, b)))
return method
def __new__(cls, *args, **kwargs):
methods = "__add__", "__sub__", "__mul__" # добавить другие
for m in methods:
setattr(cls, m, cls.method_factory(m))
return super().__new__(cls)
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __iter__(self):
# для упаковки в zip
yield from (self.x, self.y)
def __str__(self):
return f'Vector: x = {self.x}, y = {self.y}'
v1 = Vector(1, 2)
v2 = Vector(2, 5)
print(v1 + v2) # Vector: x = 3, y = 7
print(v1 - v2) # Vector: x = -1, y = -3
print(v1 * v2) # Vector: x = 2, y = 10