Question: Is there a way to (+=
and -=
) call (insert_knot
and remove_knot
) in the class bellow?
EDIT: It's not duplicated from this question, cause I'm changing an attribute of the class (summing and subtracting), not the instance by itself. I mean, I sum/subtract an attribute and a method of the instance should be called.
class SplineCurve:
def __init__(self, knot_vector: Tuple[float]):
self.knot_vector = knot_vector
@property
def degree(self) -> int:
return self.__degree
@property
def knot_vector(self) -> Tuple[float]:
return tuple(self.__knot_vector)
@degree.setter
def degree(self, new_value: int):
if new_value == self.degree:
return
if new_value > self.degree:
self.increase_degree(new_value - self.degree)
else:
self.decrease_degree(self.degree - new_value)
self.__degree = new_value
@knot_vector.setter
def knot_vector(self, new_value: Tuple[float]):
new_value = list(new_value)
new_value.sort() # Shouldn't be here, only in 'insert_knot'
self.__knot_vector = tuple(new_value)
def increase_degree(self, times: int):
self.compute_ctrl_points()
def decrease_degree(self, times: int):
self.compute_ctrl_points()
def insert_knot(self, knots: Tuple[float]):
print(f"Inserting knots {knots}")
new_knot_vector = list(self.knot_vector)
for knot in knots:
new_knot_vector += [knot]
new_knot_vector.sort()
self.knot_vector = new_knot_vector
self.compute_ctrl_points()
def remove_knot(self, knots: Tuple[float]):
print(f"Removing knots {knots}")
new_knot_vector = list(self.knot_vector)
for knot in knots:
new_knot_vector.remove(knot)
self.knot_vector = new_knot_vector
self.compute_ctrl_points()
def compute_ctrl_points(self):
print("I must be called on insertion and remotion")
Then I want to the user do it:
mycurve = SplineCurve([0, 0, 1, 1])
print(mycurve.knot_vector) # (0, 0, 1, 1)
mycurve.knot_vector += (0.5, 0.5) # This line should called as 'mycurve.insert_knot((0.5, 0.5))'
print(mycurve.knot_vector) # (0, 0, 0.5, 0.5, 1, 1)
mycurve.knot_vector -= (0.5, 1) # This line should called as 'mycurve.remove_knot((0.5, 1))'
print(mycurve.knot_vector) # (0, 0, 0.5, 1)
For inserting knot, the printed value is correct, but the function insert_knot
(and Inserting knots ...
is not printed).
But for -=
gives the error TypeError: unsupported operand type(s) for -=: 'tuple' and 'tuple'
, which is normal, cause it's like
temp_value = mycurve.knot_vector - (0.5, 1) # Error here
mycurve.knot_vector = temp_value # Setter is only called here
Calling the argument on insert_knot
and remove_knot
should allow also:
mycurve.knot_vector += numpy.array([0.3, 0.7])
mycurve.knot_vector += [0.4, 0.9]
The solution as proposed @interjay is to create a new class object that can sum with an iterable.
Then there's two possibilities:
curve.insert_knot
on __iadd__
and curve.remove_knot
on __isub__
using indirect reference, and then lose the curve's reference. We can return None
and curve.knot_vector setter
does nothing.class KnotVector(Tuple): def __new__(cls, curve, args: tuple[float]): self = super(KnotVector, cls).__new__(cls, tuple(args)) self._curve = curve return self def __iadd__(self, knots: tuple[float]): if self._curve is not None: self._curve.insert_knot(knots) self._curve = None else: return tuple(self) + knots def __isub__(self, knots: tuple[float]): if self._curve is not None: self._curve.remove_knot(knots) self._curve = None else: return tuple(self) + knots class SplineCurve: ... @property def knot_vector(self) -> KnotVector: return KnotVector(self, tuple(self._knot_vector)) @knot_vector.setter def knot_vector(self, new_knot_vector: Tuple[float]): if new_value is None: return self._knot_vector = tuple(new_knot_vector) ...
A problem that can happen is assigning
knot_vector
into a variablemyvector
, and then it changesmycurve
without noticing.mycurve = SplineCurve([0, 0, 1, 1]) myvector = mycurve.knot_vector # ... after many instructions myvector += (0.5, 0.5) # This will change mycurve
KnotVector
class to add and remove, and give the information to mycurve
through knot_vector setter
, the same way that happens for degree
:class KnotVector(Tuple): def __new__(cls, args: tuple[float]): return super(KnotVector, cls).__new__(cls, tuple(args)) def __iadd__(self, knots: tuple[float]): new_knot_vector = list(self) for knot in knots: new_knot_vector += [knot] new_knot_vector.sort() return tuple(new_knot_vector) def __isub__(self, knots: tuple[float]): new_knot_vector = list(self) for knot in knots: new_knot_vector.remove(knot) return tuple(new_knot_vector) class SplineCurve: ... @property def knot_vector(self) -> KnotVector: return KnotVector(self._knot_vector) @knot_vector.setter def knot_vector(self, new_knot_vector: Tuple[float]): if self._knot_vector == new_knot_vector: return if self.new_knots_inserted(new_knot_vector): self.insert_knot(self.get_new_knots(new_knot_vector)) else: self.remove_knot(self.get_removed_knots(new_knot_vector)) ...