为什么 python 属性() 函数被分配给 class 变量而不是实例变量?

Why python property() function is assigned to a class variable and not an instance variable?

我正在学习 python 中的封装和抽象,我遇到了 属性 函数和装饰器。常见的例子是这样的。

class Celsius():
    def __init__(self, temperature = 0):
        self.set_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)

我不明白为什么 属性 函数将描述符分配给 temperature 而不是 self.temperature。不是应该为实例创建 getter 或 setter 功能,而不是 class 吗?

类似

self.temperature = property(get_temperature,set_temperature)

使用

test = Celsius()
pprint(test.__dict__)

returns 实例对象只有 self._temperature 属性(我们试图将其设为私有)。 使用 pprint(Celsius.__dict__) returns 实际上是 class 具有我们在使用对象时访问的温度属性,据我所知这没有意义,因为我对创建功能感兴趣到实例并访问实例属性,而不是 class 属性。

提前致谢:)

There's no self outside the method. This is defining a class attribute.

@Barmar 上面的评论很有帮助。

常用的 class 函数参数 selfcls 在 class 函数之外不可用。在这种情况下,您正在定义 class 的静态成员。默认情况下,以这种方式定义的变量(如您的 temperature =)是 class.

的静态成员

在此处查看有关静态成员的一些有用提示:Are static class variables possible in Python?

But why is the syntax like that. Isnt should be assigning the property to a instance variable?

请注意以下内容。 class_static_var = 4 的行为类似于某人对 cls.class_static_var = 4 的看法,但与 self.instance_var = 4 不同。要定义实例变量,您可以使用 __init__ 方法

class DemoClass:
    def __init__(self): 
        self.instance_var = 3
    class_static_var = 4

你没有解释 为什么 你认为描述符必须在实例命名空间中,但看起来 at the documentation:

A descriptor is what we call any object that defines __get__(), __set__(), or __delete__(). ... Descriptors only work when used as class variables. When put in instances, they have no effect.

因此,要使 property 正常工作,它必须是 class 的成员 ,而不是 实例.

如果你有一个描述符,Descriptor,和一个class Foo:

class Foo:
    bar = Descriptor()

foo = Foo()

然后将在

上调用描述符协议
Foo.bar

foo.bar

无论如何,这是一件好事。每个实例都不需要携带对描述符的引用。就像方法一样,它将属于 class 但实例可以访问它。