构建多方法的 pythonic 方式 setter

The pythonic way to construct a multimethod setter

我们可以用一个@property构造一个getter和setter。这是我们如何做到这一点的简短示例:

class A:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 100:
            self.__x = 100
        else:
            self.__x = x

我的情况好像比较复杂

class A:

    def __init__(self, x):
        self.__x = x
        self.x1()
        self.x2()
        self.x3()

    def x1(self):
        self.__x1 = self.__x + 1
        return self.__x1

    def x2(self):
        self.__x2 = self.__x1 + 2
        return self.__x2

    def x3(self):
        self.__x3 = self.__x2 + 3
        return self.__x3


if __name__ == "__main__":
    a = A(3)
    print(a.x3)

方法 x1x2x3 过于简单化。 self.__x3 变量仅在调用 __init__ 方法时设置一次。现在,我需要一个 getter 方法来通过调用 a.x3 来获取 self.__x3。如何以 pythonic 方式实现?

基于您希望仅在 __init__ 期间修改 __x# 变量并且永远不会再次修改的假设来尝试回答,而且还希望访问器遵循相同的代码路径(可能是因为read 在编程上也很复杂):

在这种情况下,您可以让实现函数采用额外的默认参数。当以属性形式访问时,它将接收默认参数,但如果显式访问 属性 的 fget 成员,则可以使用 non-default 参数调用它。一个仅针对 x1 的简单示例:

class A:

    def __init__(self, x):
        self.__x = x
        # Access the property itself off the class, bypassing execution,
        # then call it directly with the non-default argument
        type(self).x1.fget(self, True)

    @property
    def x1(self, doset=False):
        if doset:
            self.__x1 = self.__x + 1
        return self.__x1

或者,为了简化 __init__ 中的用法,您可以为底层函数使用单独的名称,而不是 property 以达到相同的效果:

class A:

    def __init__(self, x):
        self.__x = x
        # Call the implementing function directly with the non-default argument
        self._x1(True)

    # Implementing function named with single underscore prefix to indicate it's
    # for internal/protected use only
    def _x1(self, doset=False):
        if doset:
            self.__x1 = self.__x + 1
        return self.__x1
    # Define property x1 based on x1 for outside use
    x1 = property(_x1)

当然,如果你没有复杂的getter路径,那么真正的解决办法是将_x1x1完全分开,其中_x1__init__ 的纯 setter 辅助函数,x1 是纯 getter:

class A:

    def __init__(self, x):
        self.__x = x
        # Call the init helper
        self._init_x1()

    # Implementing function named with single underscore prefix to indicate it's
    # for internal/protected use only
    def _init_x1(self):
        self.__x1 = self.__x + 1

    @property:
    def x1(self):
        return self.__x1

明确地说,只有最后一个在任何意义上都是 "Pythonic"。第二个选项有一些有限的用例(你有一个需要存在的函数,并且是高度可配置的,但有一组合理的默认值 property 可以使用),但在那种情况下,它通常是一个函数与 property 一样具有 public 实用性。选项 #1 是最少的 Pythonic,因为它使用起来不方便(需要提升到 class 类型,提取 fget 成员,并显式传递 self),并且非常清楚__init__ 之外没有预期的用例(因为使用起来很痛苦,没有人会打扰)。