为何类的属性和类实例的属性不相等?
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.x
和 y.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.x
和 y.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 = 2print(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