如果您不传递任何值,而是在调用方法时根据当前值计算值,那么描述动作的动词是“更新”是合理的 - 因此update_log_paths()。
请仔细检查您是否真的需要此设计,以及您/您班级的其他用户忘记调用这些“更新”方法的可能性有多大。
Python 的自省很容易允许采用“reactive programing”中的一些元素,当它们所依赖的值发生更改时,这些元素可用于触发这些更新器方法。
这种架构的一个最佳选择是属性描述符,在调用 __set__ 时将检查类级注册表以“查看”是否应该触发事件,然后使用一个装饰器,使您能够列出会触发它的属性。具有适当__init_subclass__ 方法的基类可以设置所有内容。
假设您将在您的类上拥有“基本属性”作为类主体中的注释属性 - 用于此工作的描述符、装饰器和基类代码可能是这样的:
from functools import wraps
from collections import ChainMap
class EventDescriptor:
def __init__(self, name, default):
self.name = name
self.default = default
def __get__(self, instance, owner):
if not instance:
return self
return instance.__dict__[self.name] if self.name in instance.__dict__ else self.default
def __set__(self, instance, value):
instance.__dict__[self.name] = value
triggers = instance._post_change_registry.get(self.name, [])
for trigger in triggers:
getattr(instance, trigger)()
def triggered_by(*args):
def decorator(func):
func._triggered_by = args
return func
return decorator
class EventPropertyMixin:
def __init_subclass__(cls, **kw):
super.__init_subclass__(**kw)
for property_name, type_ in cls.__annotations__.items():
if not hasattr(cls, property_name):
raise TypeError("Properties without default values not supported in this example code")
# It would also be trivial to implement runtime type-checking in this point (and on the descriptor code)
setattr(cls, property_name, EventDescriptor(property_name, getattr(cls, property_name)))
# collects all registries in ancestor-classes, preserving order:
post_change_registry = ChainMap()
for ancestor in cls.__mro__[:0:-1]:
if hasattr(ancestor, "_post_change_registry"):
post_change_registry = post_change_registy.new_child(ancestor._post_change_registry)
post_change_registry = post_change_registry.new_child({})
for method_name, method in cls.__dict__.items():
if callable(method) and hasattr(method, "_triggered_by"):
for property_name in method._triggered_by:
triggers = post_change_registry.setdefault(property_name, [])
if method_name not in triggers:
triggers.append(method_name)
cls._post_change_registry = post_change_registry
class Test(EventPropertyMixin):
path1: str = ""
path2: str = ""
@triggered_by("path1", "path2")
def update_log_paths(self):
self.log_paths = self.path1 + self.path2
让我们开始吧:
In [2]: t = Test()
In [3]: t.path1 = "/tmp"
In [4]: t.path2 = "/inner"
In [5]: t.log_paths
Out[5]: '/tmp/inner'
所以,这是复杂的代码,但代码通常位于框架内或基本实用程序库中 - 有了这 50 行代码,您可以使用 Python 为您工作,并让它调用更新方法,所以它们的名字根本不重要! :-)
(好吧,对于所提出的问题,这段代码有点矫枉过正 - 但我有心情在今晚睡觉前产生这样的东西 - 免责声明:我没有测试这里介绍的与继承相关的极端案例)