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
相关产品推荐
相关产品推荐

