如何在类变量变更时调用特定函数?

0 投票
2 回答
41 浏览
提问于 2025-04-14 15:27

我创建了一个类,并初始化了三个变量 a、b 和 c。现在我想要在外部修改变量 a 或 c 时,自动调用一个特定的函数 func1,而在外部修改变量 b 时,调用另一个函数 func2

我知道可以使用装饰器来实现,比如这样:

class Event:
    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c

    @property
    def a(self):
        return self._a
    @a.setter
    def a(self, value):
        self._a = value
        print("Variable a changed!")
        self.func1()

    @property
    def b(self):
        return self._b
    @b.setter
    def b(self, value):
        self._b = value
        print("Variable b changed!")
        self.func2()

    @property
    def c(self):
        return self._c
    @c.setter
    def c(self, value):
        self._c = value
        print("Variable c changed!")
        self.func1()

    def func1(self):
        print("Function 1 called")
    def func2(self):
        print("Function 2 called")

obj = Event(1, 2, 3)
obj.a = 15
obj.b = 10
obj.c = 5

不过,我的最终代码会有 8 个或更多的变量,给每一个变量都写一个单独的 @property 和 @var.setter 会非常麻烦,而且可读性也不高。

有没有更简单的方法,可以直接说 如果变量 a、c、f 等被更新,就调用函数 X;如果 b、e 等被更新,就调用函数 Y

谢谢!

2 个回答

1

你可以定义一个像属性一样的描述符,然后用它来定义每个变量。

class TriggerProperty:
    def __init__(self, callback):
        self.callback = callback

    def __set_name__(self, obj, name):
        self.private_name = "_" + name
        self.public_name = name

    def __get__(self, obj, obj_type):
        return getattr(obj, self.private_name)

    def __set__(self, obj, v):
        # Don't do this if the attribute is being created
        if hasattr(obj, self.private_name):
            self.callback()
            print(f'{self.public_name} changed!')

        return setattr(obj, self.private_name, v)

def func1():
    print("Function 1 called")

def func2():
    print("Function 2 called")

class Event:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    a = TriggerProperty(func1)
    b = TriggerProperty(func2)
    c = TriggerProperty(func1)

然后

>>> e = Event(1,2,3)
>>> e.a = 5
Function 1 called
a changed!
>>> e.b = 7
Function 2 called
b changed!
>>> e.c = 9
Function 1 called
c changed!

描述符的使用方法提供了一个例子,展示了如何在纯Python中定义property。你可以研究一下,看看是否有办法将TriggerProperty定义为property的子类,而不是像上面那样明确实现描述符协议。

0

你可以通过创建一个子类来扩展 property 的功能。下面是一个 基本 的例子,展示了怎么做。请注意,你需要提供一个字典,这个字典用来映射哪个属性会触发哪个函数(以字符串形式表示)。

class TriggerProperty(property):

    MAPPER = {'a': 'func1', 'b': 'func2', 'c': 'func1'}
    
    def __set__(self, obj, value):
        super().__set__(obj, value)
        func_name = self.MAPPER.get(self.fget.__name__)
        getattr(obj, func_name)()


class Event:
    # ...
    @TriggerProperty
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self._a = value
        print("Variable a changed!")

    # ...

撰写回答