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

Python中修改类列表属性时通过__set__和__setitem__实现自动更新

你的问题出在描述符的__get__返回了普通列表,当执行w.ax_mask[0] = row_mask时,直接修改的是列表本身,完全绕过了描述符的__setitem__方法,因此不会触发update(),导致window没有更新。

以下是两种可行的解决思路:


方案一:自定义可观察列表类

创建一个继承自list的子类,重写所有会修改列表的方法(比如__setitem__、append、pop等),在修改完成后自动调用宿主Window对象的update()方法。

完整代码实现

import numpy as np

class ObservedList(list):
    def __init__(self, iterable, host):
        super().__init__(iterable)
        self.host = host  # 保存宿主Window对象,用于触发更新

    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        self.host.update()  # 修改元素后触发更新

    # 可选:重写其他会修改列表的方法,确保所有修改都触发更新
    def append(self, item):
        super().append(item)
        self.host.update()

    def extend(self, iterable):
        super().extend(iterable)
        self.host.update()

    def pop(self, index=-1):
        result = super().pop(index)
        self.host.update()
        return result

    def remove(self, item):
        super().remove(item)
        self.host.update()

class AxMask:
    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = '_' + name

    def __get__(self, obj, objtype=None):
        return getattr(obj, self.private_name)

    def __set__(self, obj, value):
        # 将普通列表转换为ObservedList,绑定宿主对象
        observed_list = ObservedList(value, obj)
        setattr(obj, self.private_name, observed_list)
        obj.update()

class Window:
    ax_mask = AxMask()

    def __init__(self, arr):
        self.arr = np.asarray(arr)
        self.window = self.arr
        shape = self.arr.shape
        # 初始化时直接用ObservedList
        self.ax_mask = [np.ones((d,), dtype=bool) for d in shape]

    def update(self):
        ix_ = np.ix_(*self.ax_mask)
        self.window = self.arr[ix_]

if __name__ == '__main__':
    m, n = (10, 5)
    test_arr = np.arange(m)[:, np.newaxis] + np.zeros((m, n), dtype=int)

    w = Window(test_arr)
    row_mask = np.ones((m,), dtype=bool)
    row_mask[5::] = False

    w.ax_mask[0] = row_mask
    print(w.window)
    # 输出符合预期:只保留前5行

方案二:用代理对象拦截列表操作

修改描述符的__get__方法,返回一个代理对象,该对象包装原始列表,拦截__setitem__操作,修改完成后触发update(),同时代理列表的其他方法。

完整代码实现

import numpy as np

class ListProxy:
    def __init__(self, original_list, host):
        self._list = original_list
        self._host = host

    def __setitem__(self, key, value):
        self._list[key] = value
        self._host.update()

    # 代理列表的其他所有方法
    def __getattr__(self, name):
        return getattr(self._list, name)

    # 让代理对象可以被解包(比如在np.ix_(*self.ax_mask)中正常工作)
    def __iter__(self):
        return iter(self._list)

    def __len__(self):
        return len(self._list)

class AxMask:
    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = '_' + name

    def __get__(self, obj, objtype=None):
        original_list = getattr(obj, self.private_name)
        # 返回代理对象,拦截修改操作
        return ListProxy(original_list, obj)

    def __set__(self, obj, value):
        setattr(obj, self.private_name, value)
        obj.update()

class Window:
    ax_mask = AxMask()

    def __init__(self, arr):
        self.arr = np.asarray(arr)
        self.window = self.arr
        shape = self.arr.shape
        self.ax_mask = [np.ones((d,), dtype=bool) for d in shape]

    def update(self):
        ix_ = np.ix_(*self.ax_mask)
        self.window = self.arr[ix_]

if __name__ == '__main__':
    m, n = (10, 5)
    test_arr = np.arange(m)[:, np.newaxis] + np.zeros((m, n), dtype=int)

    w = Window(test_arr)
    row_mask = np.ones((m,), dtype=bool)
    row_mask[5::] = False

    w.ax_mask[0] = row_mask
    print(w.window)
    # 输出符合预期:只保留前5行

两种方案对比

  • 方案一:适合需要监控所有列表修改操作(比如添加、删除元素)的场景,功能全面,但需要重写多个列表方法。
  • 方案二:更轻量,仅拦截__setitem__(如果需要可以扩展拦截其他方法),通过代理模式减少代码量,适合只需要监控元素赋值的场景。

内容的提问来源于stack exchange,提问作者fuhrmanj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:34:52