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
以你第一次为例子:
能明显看到,你这次的实例化使用,都在使用
Foo
这个描述符的__get__
和__set__
方法,由于这俩方法,你都用的pass,所以Bar.x
和y.x
返回都是None,故而相等。来看第二次:
设置的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
替换成常规数据类型的时候,类的属性和实例的属性就分开了,彼此互不干扰。你可以看看官方文档中对描述符的调用描述 -> descriptor-invocation