为什么python类属性的语义在分配给实例后会发生变化?

为什么python类属性的语义在分配给实例后会发生变化?,python,class,attributes,Python,Class,Attributes,我是Python新手,对我看到的一些行为感到困惑。如果我定义 class Obj(): a = 1 b = 2 然后创建一个实例 x = Obj() 一切都很好: print(Obj.__dict__) print(x.__dict__) print(x.a,Obj.a) 产生 {'a': 1, '__module__': '__main__', 'b': 2, '__doc__': None} {} (1, 1) 但是如果我 x.a = 20 我明白了 我知道,最初,

我是Python新手,对我看到的一些行为感到困惑。如果我定义

class Obj():
    a = 1
    b = 2
然后创建一个实例

x = Obj()
一切都很好:

print(Obj.__dict__)
print(x.__dict__)
print(x.a,Obj.a)
产生

{'a': 1, '__module__': '__main__', 'b': 2, '__doc__': None}
{}
(1, 1)
但是如果我

x.a = 20
我明白了

我知道,最初,
a
是类变量
Foo.a
,并且最初没有实例变量
a
。我也明白,在这一点上
x.a
仅指对象a。我还了解,当我分配给
x.a
时,我创建了一个实例变量
x.a
,它屏蔽了类变量
Obj.a
。但是,虽然我理解这与Python关于赋值的声明的其余部分(至少部分)是一致的,但我对为什么类允许这样做感到困惑?天真地说,我本以为对
x.a
的赋值应该更改
Foo.a
(因为到目前为止它所指的就是这样),或者在赋值之前引用
x.a
应该是一个错误(例如,“没有实例属性'a'))

为什么允许这种行为。对于我们这些Python新手来说,这种行为在其他OO范式中对应什么


换句话说:我想不出任何地方你可以说
x.a
,让它表示
Obj.a
,然后在下一行,说
x.a=…
,让它不再表示
Obj.a
。有可能我只是不记得了,举个例子——或者确认没有这样的事情——将是一个完美的答案。

你已经完美地总结了语义。它是这样工作的,因为:

  • 在对象上查找属性必须返回对象属性,怎么可能不返回呢

  • 如果对象上不存在类属性,则查找对象上的属性必须找到该类属性,因为这就是对象获取方法以及类上定义的任何其他内容的方式

  • 在对象上设置属性必须创建对象属性

  • 由于Python中没有声明,因此创建属性的唯一方法是实际设置它,因此#3必须在对象上设置属性

  • 如果没有你描述的行为,我看不到一种方法可以让1到4起作用

    我不知道如何用其他语言来描述这一点。你会说,“如何用面向对象的术语来思考这个问题。”不要陷入假设其他语言“定义”了“面向对象”的陷阱。在所有语言中,面向对象的方式都是不同的

    添加了:你说

    我本以为对x.a的赋值应该更改Foo.a(因为到目前为止它一直在引用Foo.a),或者在赋值之前引用x.a应该是一个错误(例如,“没有实例属性‘a’”)


    因为Python没有声明,所以属性在分配给之前是不存在的。如果分配给对象上的属性并没有创建对象属性,您将如何创建对象属性?也就是说,通过一个没有
    x
    属性的类的例子,以及该类的一个实例,
    o.x=17
    会做什么?

    注意,关于这里发生的事情有很多答案;这个问题是关于为什么(考虑到存在明显的bug的可能性),特别是如何用面向对象的术语来思考这个问题。听起来你希望分配给
    x.a
    来改变
    x
    的类。实际上,它变异了实例
    x
    ,而不是类;
    Obj
    的任何新实例都将符合类中的模板。你能更详细地解释一下导致你做出这一假设的逻辑吗?@poorsod:我补充了一点,以澄清我所寻找的答案。我补充说,你也可以删除属性,这可能会导致一些很好的情况,如果没有当前的语义,将导致许多问题或更复杂的语义。但这有点像说“因为这就是它的实现方式”。关于OO的总体观点是好的,但是可能还有其他语言的例子可以做同样的事情。我想不出任何地方你可以说
    x.a
    并让它表示
    Obj.a
    ,然后在下一行中,说
    x.a=…
    并让它不再表示
    Obj.a
    。有可能我只是不记得了,举个例子就是一个完美的答案。@raxacoricofallapatorius:我不明白为什么另一种语言会让你对这个问题感到更舒服。为什么“Java是否喜欢xxx”比“Python是否喜欢xxx”更让人高兴?其他流行语言,特别是C++和java,是静态类型的,这意味着它们有声明在任何访问之前创建属性。Python没有。这是一个巨大的差异,并导致了语言其他部分的巨大差异。我已经在上面的第4页强调了这一点。@NedBatchelder:这是一个很好的观点。如果C++、java和SimalTalk不这样做(甚至可能讨厌它),那么这就是我要寻找的答案的一大部分。请记住,我是Python新手,现在有点像WTF:这可能会导致可怕的bug。@raxacoricofallapatorius从未听说过由此产生的bug。如果您正在处理一个类属性,那么您已经知道必须对该类而不是实例执行操作。
    A.attribute
    可能返回
    A.\uuuu class\uuuuuu.attribute
    这一事实的发生仅仅是因为方法是属于函数的类属性,因此不允许这种查找将导致类似于
    class.method的代码(实例,OtherClass.an\u other\u方法(实例2,值2,值1)
    而不是
    实例方法(instance2.an其他方法(value2))
    {'a': 1, '__module__': '__main__', 'b': 2, '__doc__': None}
    {'a': 20}
    (20, 1)