【问题标题】:how to implement @property如何实现@property
【发布时间】:2018-03-27 00:29:01
【问题描述】:

我正在比较 python 中 @property 的三个略有不同的实现。 Python 文档和“Source 1”初始化私有变量_var_name。此外,Source 1 中的代码有一个错误;初始化时它不访问.setter。相比之下,第三个示例正确初始化了公共变量x

是否有充分的理由初始化_x 来代替__init__ 中的x?这些之间是否还有我没有描述的其他差异?

来自docs

class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Source 1

class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self._temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

Source 2

class P:

    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 > 1000:
            self.__x = 1000
        else:
            self.__x = x

【问题讨论】:

标签: python decorator


【解决方案1】:

是否有充分的理由初始化 __x_x 来代替 __init__ 中的 x

属性通常用于以某种方式转换输入。内部方法(包括__init__)通常已经拥有转换后的数据,并且不希望它再次转换。例如,考虑这个有点傻但很明显的例子:

class C:
    # ...
    def __init__(self):
        f = open(C.default_filename, 'rb')
        # ...
        self._file = f
    @property
    def file(self):
        return self._file.__name__
    @file.setter
    def file(self, filename):
        self._file = open(f, 'rb')

即使您没有做任何可能错误通过 setter 的操作,内部代码通常也知道类不变量,因此 setter 所做的检查可能会产生额外的开销而无济于事.例如,如果您想要一种将温度设置为 0°C 的方法,则只需设置 self._x = 0 而不是 self.x = 0,因为您知道不需要检查 0

另一方面,某些内部方法可能希望像公众一样看到x。在这种情况下,它应该使用属性而不是底层属性。事实上,你的 Source 1 就是一个完美的例子——__init__ 只是将它的参数直接保存到_temperature,允许你构建低于绝对 0 的温度(这很糟糕,因为它实际上比无穷大更热,而且 CPU 喜欢冷)。在__init__ 中重复您已经在temperature.setter 中编写的相同前置条件测试将是愚蠢的;在这种情况下,只需设置 self.temperature


使用单下划线还是双下划线还有其他区别。

单个下划线使属性“按约定私有”;双下划线更进一步和mangles the name,这意味着它不会被你的类代码之外的意外访问。

在您的实例上使用obj._xobj.__x 提高 AttributeError。但它只能防止意外访问——如果他们真的想获得它,他们仍然可以使用obj._C__x。这样做的主要原因是为了防止子类或超类意外使用相同的名称。

【讨论】:

  • 好的。那么您如何建议解决示例“Source 1”中的问题,其中c = Celsius(-100) 不调用setter
  • @anon01 正如我在答案中所说,这是一个很好的情况,您可能确实想调用 setter — 只需调用self.temperature = temperature。 (虽然在答案中,我把它混合起来并称之为x vs. _x 而不是temperature vs. _temperature;我会解决这个问题......)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-21
  • 1970-01-01
  • 1970-01-01
  • 2014-10-01
相关资源
最近更新 更多