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

Python变量变更追踪:如何访问描述符类的属性与方法

解决方案

你的核心问题在于描述符的实现逻辑错误:原TrackedValidatedInteger作为类属性,所有Table实例会共享同一个描述符对象的value和has_changed,不仅会导致实例间状态互相干扰,还因为__get__直接返回值,无法访问描述符的其他方法。

下面提供两种可行的实现方案,均保留p.length = 3的简洁赋值方式:


方案一:修正描述符实现(推荐,适合多参数场景)

重新设计描述符,将每个实例的状态(值、变更标记)存储在实例自身的__dict__中,同时通过类访问描述符时返回其自身,以便调用方法:

class TrackedValidatedInteger:
    def __init__(self, min_value=None, max_value=None):
        self.min_value = min_value
        self.max_value = max_value
        # 生成唯一键,用于在实例中存储值和变更状态
        self._value_key = f"_tvint_{id(self)}_value"
        self._changed_key = f"_tvint_{id(self)}_changed"

    def __get__(self, obj, objtype=None):
        if obj is None:
            # 通过类访问时,返回描述符自身,方便调用方法
            return self
        # 通过实例访问时,返回存储的值
        return getattr(obj, self._value_key, None)

    def __set__(self, obj, value):
        if self._validate(value):
            setattr(obj, self._value_key, value)
            setattr(obj, self._changed_key, True)
            return 1
        return 0

    def get_has_changed(self, obj):
        # 获取指定实例的变更状态
        return getattr(obj, self._changed_key, False)
    
    def reset_has_changed(self, obj):
        # 重置指定实例的变更状态
        setattr(obj, self._changed_key, False)
        
    def _validate(self, value):
        if self.min_value is not None and value < self.min_value:
            print(f"值必须在 {self.min_value} 到 {self.max_value} 之间")
            return 0
        if self.max_value is not None and value > self.max_value:
            print(f"值必须在 {self.min_value} 到 {self.max_value} 之间")
            return 0
        return 1

class Table():
    length = TrackedValidatedInteger(min_value=0, max_value=3)
    height = TrackedValidatedInteger(min_value=0, max_value=6)
    width = TrackedValidatedInteger(min_value=0, max_value=7)
    
    def __init__(self, length=0, height=0, width=0):
        self.length = length
        self.height = height
        self.width = width 

    def get_changed_params(self):
        # 获取所有已变更的参数及其值
        changes = {}
        for name, descriptor in vars(type(self)).items():
            if isinstance(descriptor, TrackedValidatedInteger):
                if descriptor.get_has_changed(self):
                    changes[name] = getattr(self, name)
        return changes
    
    def reset_all_changes(self):
        # 重置所有参数的变更标记
        for name, descriptor in vars(type(self)).items():
            if isinstance(descriptor, TrackedValidatedInteger):
                descriptor.reset_has_changed(self)

使用示例:

p = Table()
p.length = 3  # 有效赋值,标记为已变更
p.length = 9  # 无效赋值,不修改状态

print(p.length)  # 输出:3
print(Table.length.get_has_changed(p))  # 输出:True
print(p.get_changed_params())  # 输出:{'length': 3}

p.reset_all_changes()
print(Table.length.get_has_changed(p))  # 输出:False
print(p.get_changed_params())  # 输出:{}

# 多个实例互不干扰
p2 = Table(length=1)
print(p.length)  # 输出:3
print(p2.length) # 输出:1
print(Table.length.get_has_changed(p2)) # 输出:True

方案二:使用@property装饰器(适合参数较少的场景)

如果参数数量不多,可以用@property手动实现每个参数的跟踪逻辑,虽然代码重复,但逻辑更直观:

class Table:
    def __init__(self, length=0, height=0, width=0):
        self._length = length
        self._length_changed = False
        self._height = height
        self._height_changed = False
        self._width = width
        self._width_changed = False

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, value):
        if 0 <= value <= 3:
            self._length = value
            self._length_changed = True
        else:
            print("值必须在0到3之间")

    @property
    def height(self):
        return self._height

    @height.setter
    def height(self, value):
        if 0 <= value <= 6:
            self._height = value
            self._height_changed = True
        else:
            print("值必须在0到6之间")

    # width的setter和getter同理,此处省略

    def get_changed_params(self):
        changes = {}
        if self._length_changed:
            changes['length'] = self.length
        if self._height_changed:
            changes['height'] = self.height
        if self._width_changed:
            changes['width'] = self.width
        return changes
    
    def reset_all_changes(self):
        self._length_changed = False
        self._height_changed = False
        self._width_changed = False

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 10:50:58