【问题标题】:Python naming convention for setter method name without args不带参数的 setter 方法名称的 Python 命名约定
【发布时间】:2019-01-16 21:58:52
【问题描述】:

我一直在编写带有类的 python 代码,这些类将有一个类似的方法:

def set_log_paths(self):

问题是,这个方法不带参数,它根据 self 的其他值确定某些值应该是什么。在这种情况下使用“设置”一词是否不合适?我之所以这么问,是因为它不是一个直接的 getter 或 setter,就像在具有私有成员的语言中使用的那样。

在我的方法名称中是否有常用的常规词?

【问题讨论】:

  • 元问题:有没有更好的地方来询问这些约定问题?我担心因意见问题而被否决。这个问题的答案可能是“没有约定”,但这并不是真正的意见。
  • 对我来说,至少这种情况表明潜在的code smell。为什么set_log_paths 不在构造函数中执行?如果那是因为稍后设置了某些值,也许这应该是其他功能的一部分?作为您班级的客户,我可能在猜测我需要调用此方法时遇到问题。只是想深入了解它。
  • @BradFallon,Meta-Meta-Question 你在 Meta 上问过这个问题吗?
  • 假设他在__init__ 中做了几个步骤,然后从那里对函数调用中的步骤进行分组以提高可读性(当然还有可读性好的函数名称)。那我看不出有什么问题。至于set 这个词,还有setup 或configure 之类的替代词。此外,如果它是该方法所做的......并且由于属性的原因,setter 和 getter 在 Python 中并不常见(按那个名称),所以即使是客户端代码用户也应该怀疑它不是传统的 setter,而是更复杂的操作.复数也有助于暗示。这当然是有争议的......

标签: python naming conventions


【解决方案1】:

如果您不传递任何值,而是在调用方法时根据当前值计算值,那么描述动作的动词是“更新”是合理的 - 因此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 为您工作,并让它调用更新方法,所以它们的名字根本不重要! :-) (好吧,对于所提出的问题,这段代码有点矫枉过正 - 但我有心情在今晚睡觉前产生这样的东西 - 免责声明:我没有测试这里介绍的与继承相关的极端案例)

【讨论】:

    猜你喜欢
    • 2010-10-01
    • 2023-04-02
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 2021-02-17
    相关资源
    最近更新 更多