首先:这与“描述”有关,而不是“方法解析顺序”,后者是为搜索超类的方法和属性而保留的表达式。
那么,针对您的具体疑问:
当我将 kelvin 属性设置为 50 时,它会调用 __set__ 方法
开尔文()正确吗?我不明白 50 的值在哪里
然后存储在这种情况下。
是的,该方法被调用。在这种情况下,不存储正确的值“50”。在这个设计中,Temperature 实例必须提供一个单一的、有效的温度测量值,无论所需的比例如何,它都可以保持一致并且可以写入和读取。为该值的内部表示选择的比例是摄氏度(尽管问题中显示的摄氏度描述符不正确,请参见下文)。因此,开尔文和华氏度的读取和写入不存储它们的值:写入将刻度转换为摄氏温度,并设置摄氏温度值,并通过__get__ 方法读取摄氏温度值和将其转换回所需的比例。换句话说:Kelvin 和 F. 的值是“计算的”。
我也不明白 instance.celsius = (value - 273.15) 在
set 在我分配 t1.kelvin=50 时被处理。
它的处理方式与任何其他对温度实例“摄氏度”的分配相同:运行Celsius.set 中的代码 - 在这种情况下,它获得的值是传递给“实例”的初始值。 kelvin" 减去 273.15:表达式 (value - 273.15) 没有什么特别之处(实际上不需要括号)。它将传递的值转换为摄氏度。 instance.celsius 指向一个描述符这一事实使得 = 运算符的行为有所不同:摄氏中的 __set__ 方法被调用 - 并接收减法运算的结果(恰好是正确的值摄氏度)
错误代码:现在,请注意一件事:描述符在类主体中被实例化 - 并且为该类的所有实例共享。这意味着Temperature 的所有实例都共享您的Celsius 类的相同 实例:因此它不能将其值作为“self”属性保存。如果您在代码中按原样创建Temperature 的两个实例,您将看到它们不是独立的,因为对所有比例的读取和写入都是在Temperature.celsius 的val 属性中执行的。
正确的做法是保留在描述符的__get__ 和__set__ 方法中收到的instance 参数中的值。
为了避免递归,这通常通过在实例上保留不同的属性名称来完成 - 这可以通过添加“_”前缀来完成。
在这种情况下,celsius 描述符只是一个“无操作”,甚至不存在。此外,由于您正在编写描述符,如果它将内部属性硬编码为“_celsius”,如本例所示,同一类中的多个描述符实例会干扰另一个:必须使用动态描述符的名称。
class Celsius():
def __init__(self, name):
self.name=name
# The name could be set automatically
# by having a __set_name__ method, but let's
# go one step at a time
def __get__(self, instance, owner):
if not instance:
#when retrieved from the class, "instance" is None, then
# return the descriptor itself,
# and do not attempt to fetch values
return self
return getattr(instance, "_" + self.name)
def __set__(self, instance, value):
setattr(instance, "_" + self.name)
除此之外,我建议您在 getter 和 setter 中添加“打印”调用并尝试它,直到您掌握您在此处询问的内容的流程:
class Celsius():
def __init__(self, name):
self.name=name
# The name could be set automatically
# by having a __set_name__ method, but let's
# go one step at a time
def __get__(self, instance, owner):
if not instance:
return self
print(f"celsius getter, picking the internal value from '_{self.name}'")
return getattr(instance, "_" + self.name)
def __set__(self, instance, value):
print(...)
setattr(instance, "_" + self.name)
class Fahrenheit():
def __get__(self, instance, owner):
if not instance:
return self
print(...)
return instance.celsius * 1.8 + 32
def __set__(self, instance, value):
print(...)
instance.celsius=(value-32)/1.8
class Kelvin():
def __get__(self, instance, owner):
if not instance:
return self
print(...)
return (instance.celsius + 273.15)
def __set__(self, instance, value):
print(...)
instance.celsius = (value - 273.15)
class Temperature():
celsius = Celsius("celsius")
fahrenheit = Fahrenheit()
kelvin = Kelvin()
最后:请记住,对于不可重用且适合单个属性的代码,例如在此示例中,Python 已经提供了property,它是一个描述符,但已经填满,因此您可以放心关于 getter 和 setter 中的逻辑,不要担心处理描述符实例本身:您的方法已经在“self”中获得对实例的引用。