如何让SplineCurve类的knot_vector通过+=/-=调用对应实例方法?
问题
能否让以下SplineCurve类中,通过+=和-=操作分别调用insert_knot和remove_knot方法?
补充说明
此问题并非重复提问,因为我们是修改类的属性(进行加减操作),而非实例本身,即对属性做加减时需触发实例的对应方法。
现有类代码
from typing import Tuple 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")
期望实现的操作
mycurve = SplineCurve([0, 0, 1, 1]) print(mycurve.knot_vector) # (0, 0, 1, 1) mycurve.knot_vector += (0.5, 0.5) # 应等效调用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) # 应等效调用mycurve.remove_knot((0.5, 1)) print(mycurve.knot_vector) # (0, 0, 0.5, 1)
当前问题
- 执行
+=插入节点时,结果值正确,但insert_knot方法未被触发(无对应打印输出); - 执行
-=操作时会抛出TypeError: unsupported operand type(s) for -=: 'tuple' and 'tuple'错误,因为本质是先执行元组相减(不支持),再调用setter赋值。
额外要求
insert_knot和remove_knot方法需支持接收numpy数组、列表等类型的参数,例如:
mycurve.knot_vector += numpy.array([0.3, 0.7]) mycurve.knot_vector += [0.4, 0.9]
解决方案
核心思路是给knot_vector属性创建一个代理类,让代理类接管+=和-=操作,将其转发给SplineCurve实例的对应方法,同时兼容多种输入类型。
修改后的完整代码
from typing import Tuple, Iterable import numpy as np class KnotVectorProxy: def __init__(self, curve: 'SplineCurve'): self.curve = curve def __iadd__(self, knots: Iterable[float]): # 统一转换为元组,适配列表、numpy数组等输入 if isinstance(knots, np.ndarray): knots = tuple(knots.tolist()) else: knots = tuple(knots) self.curve.insert_knot(knots) return self def __isub__(self, knots: Iterable[float]): if isinstance(knots, np.ndarray): knots = tuple(knots.tolist()) else: knots = tuple(knots) self.curve.remove_knot(knots) return self def __repr__(self): return repr(self.curve._get_knot_vector()) class SplineCurve: def __init__(self, knot_vector: Tuple[float]): # 初始化内部节点向量,直接排序(替代原setter的逻辑) self.__knot_vector = tuple(sorted(knot_vector)) # 补全原代码缺失的degree初始化 self.__degree = 1 @property def degree(self) -> int: return self.__degree @property def knot_vector(self) -> KnotVectorProxy: # 返回代理对象,而非直接返回元组 return KnotVectorProxy(self) @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 # 内部方法:获取原始节点向量 def _get_knot_vector(self) -> Tuple[float]: return self.__knot_vector # 内部方法:设置节点向量 def _set_knot_vector(self, new_value: Tuple[float]): 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.append(knot) new_knot_vector.sort() self._set_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: if knot in new_knot_vector: new_knot_vector.remove(knot) self._set_knot_vector(new_knot_vector) self.compute_ctrl_points() def compute_ctrl_points(self): print("I must be called on insertion and remotion")
关键修改说明
代理类
KnotVectorProxy:- 持有
SplineCurve实例引用,通过__iadd__转发+=操作到insert_knot,__isub__转发-=操作到remove_knot。 - 自动转换列表、numpy数组等输入为元组,满足方法参数要求。
- 实现
__repr__方法,保证打印knot_vector时行为和原代码一致。
- 持有
SplineCurve类调整:knot_vector属性不再返回元组,而是返回代理对象,让代理接管操作逻辑。- 移除原有的
knot_vectorsetter,改用内部方法_set_knot_vector维护内部状态,避免外部直接修改。 - 修复原代码中
__degree未初始化的问题,补充默认值。 - 在
remove_knot中增加节点存在性判断,避免因节点不存在抛出异常。
测试验证
mycurve = SplineCurve([0, 0, 1, 1]) print(mycurve.knot_vector) # (0, 0, 1, 1) mycurve.knot_vector += (0.5, 0.5) # 输出: Inserting knots (0.5, 0.5) / I must be called on insertion and remotion print(mycurve.knot_vector) # (0, 0, 0.5, 0.5, 1, 1) mycurve.knot_vector -= (0.5, 1) # 输出: Removing knots (0.5, 1) / I must be called on insertion and remotion print(mycurve.knot_vector) # (0, 0, 0.5, 1) # 测试多类型输入 import numpy as np mycurve.knot_vector += np.array([0.3, 0.7]) # 输出: Inserting knots (0.3, 0.7) / ... print(mycurve.knot_vector) # (0, 0, 0.3, 0.5, 0.7, 1) mycurve.knot_vector += [0.4, 0.9] # 输出: Inserting knots (0.4, 0.9) / ... print(mycurve.knot_vector) # (0, 0, 0.3, 0.4, 0.5, 0.7, 0.9, 1)
内容的提问来源于stack exchange,提问作者Carlos Adir
相关产品推荐
相关产品推荐

