我在python中比较了@property
的三个稍微不同的实现。 Python文档和" Source 1"初始化私有变量_var_name
。此外,Source 1的代码有一个bug;初始化时,它无法访问.setter
。相比之下,第三个示例正确初始化公共变量x
。
是否有充分理由在_x
中初始化x
代替__init__
?我还没有描述这些之间的其他差异吗?
来自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
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
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
答案 0 :(得分:1)
是否有充分理由在
__x
中初始化_x
或x
代替__init__
?
属性通常用于以某种方式转换输入。内部方法(包括__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._x
适用于您的实例; obj.__x
提出AttributeError
。但它只能阻止意外访问 - 如果他们真的想要获取它,他们仍然可以使用obj._C__x
。这样做的主要原因是为了保护子类或超类不会意外地使用相同的名称。