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

如何在Python装饰器中创建描述符实例以监听属性变更?

解决Python属性监听的装饰器改造方案

不用字符串传属性名的核心思路是利用占位变量的__name__属性提取属性名,再通过装饰器动态给目标类注入描述符实例。下面是完整实现步骤:

1. 完善描述符类(自动绑定属性名)

优化你的ObservedAccess描述符,利用Python 3.6+支持的__set_name__方法,自动把属性名绑定到描述符实例上,省去手动传递的麻烦:

class ObservedAccess:
    def __init__(self):
        # 存储属性更新回调,用于后续触发GUI更新等操作
        self.update_callbacks = []

    def __get__(self, instance, owner):
        # 从实例的__dict__取值,避免触发描述符无限递归
        return instance.__dict__.get(self.name, None)

    def __set__(self, instance, value):
        old_val = instance.__dict__.get(self.name, None)
        instance.__dict__[self.name] = value
        # 触发所有注册的回调
        for callback in self.update_callbacks:
            callback(attr_name=self.name, old_value=old_val, new_value=value)

    def __set_name__(self, owner, name):
        # 自动绑定属性名到描述符实例
        self.name = name

    def on_update(self, callback):
        # 提供注册回调的接口
        self.update_callbacks.append(callback)

2. 实现observableclass装饰器

装饰器接收占位变量作为参数,通过__name__提取属性名,动态给类注入ObservedAccess实例:

def observableclass(*attr_placeholders):
    def decorator(cls):
        for placeholder in attr_placeholders:
            # 从占位变量的__name__获取属性名
            attr_name = placeholder.__name__
            # 给类注入描述符实例
            setattr(cls, attr_name, ObservedAccess())
        return cls
    return decorator

3. 用法示例

先定义占位变量(名字对应要监听的属性名),再用装饰器修饰类:

# 定义占位变量,名字就是要监听的属性名
some_int = None
some_string = None

@observableclass(some_int, some_string)
class TestObserving:
    pass

# 测试:注册回调并修改属性
def gui_update(attr_name, old_value, new_value):
    print(f"GUI更新:属性[{attr_name}] 从 {old_value} 变为 {new_value}")

obj = TestObserving()
# 给some_int属性注册GUI更新回调
TestObserving.some_int.on_update(gui_update)
# 修改属性触发回调
obj.some_int = 20
obj.some_string = "测试文本"

额外优化:避免占位变量污染命名空间

如果不想在全局定义占位变量,可以用辅助函数生成带名字的占位符:

def attr(name):
    """生成属性占位符的辅助函数"""
    def placeholder():
        pass
    placeholder.__name__ = name
    return placeholder

# 用法简化,无需全局变量
@observableclass(attr("some_int"), attr("some_string"))
class TestObserving:
    pass

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 10:54:59