为何类的属性和类实例的属性不相等?

为何类的属性和类实例的属性不相等?

class Foo:   #描述符

def __get__(self, instance, owner):

pass

def __set__(self, instance, value):

pass

class Bar:

x = Foo() #把描述符代理一个类的属性

def __init__(self,n):

self.x = n

上面的代码准备好了两个类,请看表演

>>> y=Bar(1)

>>> y.x

>>> Bar.x

>>> Bar.x == y.x

True

接着来

>>> Bar.x = 2

>>> y.x

2

>>> Bar.x

2

>>> y=Bar(3)

>>> y.x

3

>>> Bar.x

> 2


回答:

class Foo:   #描述符

def __get__(self, instance, owner):

print("tigger __get__") # <- Add print for debugging

pass

def __set__(self, instance, value):

print("tigger __set__") # <- Add print for debugging

pass

class Bar:

x = Foo() #把描述符代理一个类的属性

def __init__(self,n):

self.x = n

以你第一次为例子:

>>> y=Bar(1)

tigger __set__

>>> y.x

tigger __get__

>>> Bar.x

tigger __get__

>>> Bar.x == y.x

tigger __get__

tigger __get__

True

能明显看到,你这次的实例化使用,都在使用 Foo 这个描述符的 __get____set__ 方法,由于这俩方法,你都用的pass,所以 Bar.xy.x 返回都是None,故而相等。

来看第二次:

>>> Bar.x = 2

>>> y.x

2

>>> Bar.x

2

>>> y=Bar(3)

>>> y.x

3

>>> Bar.x

> 2

设置的print均没有打印,这是因为原本类属性 x 是描述符类 Foo,但是 Bar.x = 2 将类属性 x 赋值给了整数2,这个操作更改了类属性 x 的id和原本数据类型,这时候 类.x实例.x 看上去虽然都是同一个属性名称 x,但内存地址已经不同了。

所以当你将y重新实例化的时候,y = Bar(3) 里面 Bar.x 不受实例属性的value变化而影响,所以 y.x 此时是3,但 Bar.x 依旧是旧值2。

这里面有个关键点就是,描述符在里面起到的作用。当类的属性值是描述符的时候,类和实例拥有相同的命名空间x,这时候描述符的lookup优先级高于类和实例的dictionary查找顺序,均以描述符为最高等级的查询机制,所以你的第一步 y = Bar(1) 的时候,Bar.xy.x 其实都是因为描述符的存在,他们二者联系在了一起,彼此相通。但当你第二步,将 Bar.x 替换成常规数据类型的时候,类的属性和实例的属性就分开了,彼此互不干扰。

  • Instance lookup scans through a chain of namespaces giving data descriptors the highest priority, followed by instance variables, then non-data descriptors, then class variables, and lastly __getattr__() if it is provided.
  • If a descriptor is found, it is invoked with desc.__get__(None, A)「A is a class」.
  • Data descriptors always override instance dictionaries.

你可以看看官方文档中对描述符的调用描述 -> descriptor-invocation


回答:

Bar.x 是类属性,它是一个描述符对象 Foo() 的实例。在实例化 y = Bar(1) 时,我们将 self.x 设置为 1。

此时,y.x 是实例 y 的属性。但是,由于我们没有定义 Foo 类的 get 方法,所以 y.x 的访问会调用 Foo 类的 get 方法,该方法中并没有返回任何值,因此输出为 None。

同时,当我们打印 Bar.x 时,输出为 <__main__.Foo object at 0x000001>。这是因为 Bar.x 是类属性,它是 Foo() 的实例,所以输出的是 Foo() 对象的内存地址。

接下来,我们执行以下代码:

Bar.x = 2

print(y.x) # 输出:2

print(Bar.x) # 输出:2

我们修改了类属性 Bar.x 的值为 2。当我们打印 y.x 时,输出为 2,这是因为此时 y.x 是实例属性,而实例属性会覆盖类属性的值。

同时,当我们打印 Bar.x 时,输出也为 2,这是因为类属性 Bar.x 的值已被修改为 2。

接下来,我们重新创建了一个实例 y=Bar(3):

y = Bar(3)

print(y.x) # 输出:3

print(Bar.x) # 输出:2

我们将实例 y 的属性 x 设置为 3。此时,y.x 是实例属性,值为 3。而 Bar.x 仍然是类属性,值为 2,没有受到实例属性的影响。

所以,最终输出为 y.x=3 和 Bar.x=2。这说明类实例的属性和类属性是不相等的。

以上是 为何类的属性和类实例的属性不相等? 的全部内容, 来源链接: utcz.com/p/938964.html

回到顶部