python - 为什么在这段代码中先定义类变量,然后再将其转换为实例变量?

标签 python python-2.7 properties

在学习python属性装饰器时 link ,我偶然发现了以下几行代码:

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

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

    def get_temperature(self):
        print("Getting value")
        return self._temperature

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

    temperature = property(get_temperature,set_temperature)

此代码基本上允许您修改/添加对 Celsius 对象的限制,而无需任何继承 Celsius 类的人重构其代码。

为什么它首先定义一个类变量Temperature,而不是直接让self.Temperature = property(get_Temperature,Set_Temperature)然后完成?

编辑:由于评论中存在意见冲突,我现在将代码恢复到原始状态,无论是否有错别字,以方便人们以后阅读。

最佳答案

答案就在 descriptor protocol and attribute lookup order 。如果您这样做:

class Celsius:
    def __init__(self, temperature): 
        self.temperature = property(get_temperature,set_temperature)
        self.temperature = temperature
# etc etc

它不会按照您期望的方式运行。完全没有。

t = Celsius(100)
t.temperature = -500 # NO ERROR! BROKEN!
为什么?因为您使用提供给初始值设定项的数字覆盖了属性对象。观察:

get = lambda *args: None # dummy getter
set = lambda *args: None # dummy setter
p = property(get, set) # dummy property

属性应该是Property的实例:

print(type(p).__name__)
# Property

但是你的温度不再是一种属性:

print(type(t.temperature).__name__)
# int

您用这一行覆盖了它:

self.temperature = temperature 
# note that temperature is an int or float

撒上一些print语句来看看发生了什么:

class Celsius:
    def __init__(self, temperature): 
        self.temperature = property(get_temperature,set_temperature)
        print(type(self.temperature).__name__) # Property
        self.temperature = temperature
        print(type(self.temperature).__name__) # no longer a Property!
# etc etc

因此,属性对象需要存储在类级别,这样它就不会在实例级别被覆盖。当在实例级别访问类级别属性对象时,会自动调用描述符协议(protocol)(Property是一种描述符;描述符具有不寻常的行为,因此请研究它们小心)。

了解有关类级对象与实例级对象的更多信息 much more detailed answer .

另请注意,需要使用该属性在初始化程序中设置温度。不要不要这样做:

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

使用此代码,您可以在没有错误的情况下执行此操作,但这是不好的:t=Celsius(-500)。通常,初始化程序应使用该属性来获取或设置私有(private)变量,就像任何其他方法一样:

        self.temperature = temperature

现在,正如预期的那样,无效的初始温度将导致错误。

关于python - 为什么在这段代码中先定义类变量,然后再将其转换为实例变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44518852/

相关文章:

javascript - 如何将 Websockets 与 Pyramid 和 socket.io 一起使用?

我的 Mac 上的 Python 很乱,帮我卸载我不需要的东西

mysql - Unicode编码错误: 'charmap' codec can't encode character u'\xfd' in python 2. 7

javascript - 使用这个。在 javascript 对象定义中

wpf - 如何在双向绑定(bind)组合框 (WPF) 上调用异步操作

python - 遇到 : json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

python - Google 表格 "repeatCell"从现有单元格中剥离格式

python - 为 emacs-jedi 安装 python 服务器

list - 字典列表中值的总和

c# - 我如何确保依赖属性(property)的其他人的属性(property)值(value)?