当前位置: 首页 > 知识库问答 >
问题:

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

宁侯林
2023-07-30
class Foo:   #描述符    def __get__(self, instance, owner):        pass    def __set__(self, instance, value):        passclass Bar:    x = Foo()      #把描述符代理一个类的属性    def __init__(self,n):          self.x = n

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

>>> y=Bar(1)>>> y.x>>> Bar.x>>> Bar.x == y.xTrue

接着来

>>> Bar.x = 2>>> y.x2>>> Bar.x 2>>> y=Bar(3)>>> y.x3>>> Bar.x> 2

共有2个答案

陆高峰
2023-07-30
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        passclass Bar:    x = Foo()      #把描述符代理一个类的属性    def __init__(self,n):          self.x = n

以你第一次为例子:

>>> y=Bar(1)tigger __set__>>> y.xtigger __get__>>> Bar.xtigger __get__>>> Bar.x == y.xtigger __get__tigger __get__True

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

来看第二次:

>>> Bar.x = 2>>> y.x2>>> Bar.x 2>>> y=Bar(3)>>> y.x3>>> Bar.x> 2

设置的print均没有打印,这是因为原本类实例 x 是描述符类 Foo,但是 Bar.x = 2 将类实例赋值给了整数2,这个操作更改了类实例的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

祁鸿晖
2023-07-30

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)  # 输出:2print(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)  # 输出:3print(Bar.x)  # 输出:2

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

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

 类似资料:
  • 主要内容:类变量(类属性),实例变量(实例属性),局部变量无论是类属性还是类方法,都无法像普通变量或者函数那样,在类的外部直接使用它们。我们可以将类看做一个独立的空间,则类属性其实就是在类体中定义的变量,类方法是在类体中定义的函数。 前面章节提到过,在类体中,根据变量定义的位置不同,以及定义的方式不同,类属性又可细分为以下 3 种类型: 类体中、所有函数之外:此范围定义的变量,称为类属性或类变量; 类体中,所有函数内部:以“self.变量名”的方式定义的

  • 问题内容: 我是python的新手,了解到类属性就像C ++中的静态数据成员一样。但是,尝试以下代码后,我感到困惑: f2.a是否也等于5? 如果将a定义为列表而不是整数,则预期行为: 我研究了 Python:类和实例属性之间的区别,但是它不能回答我的问题。 谁能解释为什么会有所不同?谢谢 问题答案: 在第二个示例中,您没有做相同的事情。在第一个示例中,您要分配一个新值: 在第二个示例中,您只是在

  • 下表列出了 4 个常见的明星人物: 姓名 年龄 周润发 58 成龙 55 刘德华 53 周星驰 54 进行归纳总结: 这些人物具有较高的知名度,把这些人物归类为明星; 每个明星两个属性:姓名和年龄。明星这个群体具有一个属性:数量,在这张表格中,明星的数量是 4; 姓名和年龄等属性是用于描述具体的一个对象(例如:周润发),而人物的数量是用于描述明星这个类别。 如果使用面向对象技术对以上实体进行描述,

  • 由于Python是动态语言,根据类创建的实例可以任意绑定属性。 给实例绑定属性的方法是通过实例变量,或者通过self变量: class Student(object): def __init__(self, name): self.name = name s = Student('Bob') s.score = 90 但是,如果Student类本身需要绑定一个属性呢?可

  • 如何使类示例推断类型基于实例值检查: 打字沙盒。

  • 我有以上代码,为什么修改类属性影响实例属性?有人会说这是由于实例共享类属性,根据mro属性解析顺序是如此,但是这是在实例命名空间没有要搜索的属性时才会访问类的命名空间,在我的代码中,实例(shili)有自己的name属性,但为什么还是共享类属性呢?或者说这是其他什么问题,求大佬解答