You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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")

关键修改说明

  1. 代理类KnotVectorProxy:

    • 持有SplineCurve实例引用,通过__iadd__转发+=操作到insert_knot,__isub__转发-=操作到remove_knot。
    • 自动转换列表、numpy数组等输入为元组,满足方法参数要求。
    • 实现__repr__方法,保证打印knot_vector时行为和原代码一致。
  2. SplineCurve类调整:

    • knot_vector属性不再返回元组,而是返回代理对象,让代理接管操作逻辑。
    • 移除原有的knot_vector setter,改用内部方法_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 19:20:33