如何在类变量变更时调用特定函数?
我创建了一个类,并初始化了三个变量 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!")
# ...