Python 如何实现@property

Python 如何实现@property,python,decorator,Python,Decorator,我比较了python中@property的三种稍微不同的实现。Python文档和“源代码1”初始化私有变量\u var\u name。此外,源代码1中的代码有一个bug;初始化时,它不访问.setter。相比之下,第三个示例正确地初始化了公共变量x 是否有充分的理由初始化\ux以代替\uuu init\uu中的x?这两者之间还有什么我没有描述的区别吗 从: : class摄氏度: def _初始(自身,温度=0): 自身温度=温度 def至华氏温度(自身): 返回(自身温度*1.8)+32 @财

我比较了python中
@property
的三种稍微不同的实现。Python文档和“源代码1”初始化私有变量
\u var\u name
。此外,源代码1中的代码有一个bug;初始化时,它不访问
.setter
。相比之下,第三个示例正确地初始化了公共变量
x

是否有充分的理由初始化
\ux
以代替
\uuu init\uu
中的
x
?这两者之间还有什么我没有描述的区别吗

从:

:

class摄氏度:
def _初始(自身,温度=0):
自身温度=温度
def至华氏温度(自身):
返回(自身温度*1.8)+32
@财产
def温度(自身):
打印(“获取价值”)
返回自身温度
@温度调节器
def温度(自身、数值):
如果值<-273:
提升值错误(“温度不可能低于-273”)
打印(“设置值”)
自身温度=数值
:

P类:
定义初始化(self,x):
self.x=x
@财产
def x(自我):
返回自我
@x、 塞特
def x(self,x):
如果x<0:
自。uux=0
elif x>1000:
自整角x=1000
其他:
自
是否有充分的理由初始化
\uuux
\ux
以代替
\uuuu init\uux
中的
x

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

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.\u x=0
而不是
self.x=0
,因为您知道
0
不需要检查

另一方面,一些内部方法可能希望以与公共方法相同的方式查看
x
。在这种情况下,它应该使用属性而不是基础属性。事实上,您的源代码1是一个完美的例子-
\uuuu init\uuuuu
只是将其参数直接保存到
\u temperature
,允许您构造绝对0以下的温度(这是不好的,因为它实际上比无穷大更热,而且CPU喜欢冷)。在
\uuuu init\uuuuu
中重复您在
temperature.setter
中编写的相同前提条件测试是愚蠢的;在这种情况下,只需设置
self.temperature


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

单个下划线使属性“按约定为私有”;双下划线更进一步,这意味着不能从类代码外部意外访问它


使用
obj.\u x
可以在您的实例上工作<代码>对象.\uuux引发
属性错误
。但这只能防止意外访问,如果他们真的想访问,他们仍然可以使用obj.\u C\u x。这样做的主要原因是为了防止子类或超类意外使用相同的名称。

另一个区别是
摄氏度。温度
是一个非常有意义的名称,而
p.x
,谁知道它是什么意思,或者为什么它被限制为0-1000。但我相信你已经知道了。那么,您建议如何解决示例“Source 1”中的问题,其中
c=Celsius(-100)
不调用
setter
?@anon01正如我在回答中所说的,这是一个很好的情况,您可能希望调用setter只需执行
self.temperature=temperature
。(虽然在回答中,我把它弄混了,称它为
x
vs.
\ux
而不是
温度
vs.
\uTemperature
;我会解决这个问题……)
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
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')