Python setter 不带参数的方法名称的命名约定

Python naming convention for setter method name without args

我一直在用 类 编写 python 代码,其中有一个方法叫做:

def set_log_paths(self):

事实是,此方法不带参数,它根据 self 的其他值确定某些值应该是什么。在这种情况下使用 "set" 这个词是不是不合适?我问是因为它不是直接的 getter 或 setter,因为人们会在具有私人成员的语言中使用它。

我的方法名称中是否有常用的约定词?

如果您不传递任何值,而是根据当前值计算调用该方法时的值,描述该操作的动词是 "update" 是合理的 - 因此update_log_paths().

仔细检查一下您是否真的需要这种设计,you/other 您的 class 用户忘记调用这些 "update" 方法的可能性有多大。

Python 的内省很容易允许采用“reactive programing”中的一些元素,当它们所依赖的值发生变化时,这些元素可用于触发这些更新方法。

这种架构的一个最佳选择是您的属性的描述符,在调用 __set__ 后,如果应该触发事件,将检查 class 级别的注册表以 "see" ,然后是一个装饰器,使您能够列出将触发它的属性。具有适当 __init_subclass__ 方法的基础 class 可以设置一切。

假设您将 class 上的 "base properties" 作为 class 正文中的注释属性 - 描述符、修饰符和基础-class 代码工作可能是一件好事:

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 来工作 for 你,让它调用更新方法,所以它们的名字根本不重要! :-) (好的,这段代码对于所问的问题来说太过分了 - 但我今晚睡觉前有心情制作这样的东西 - 免责声明:我没有测试此处涵盖的与继承相关的角落案例)