为什么python类属性的语义在分配给实例后会发生变化?
我是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 我明白了 我知道,最初,
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没有声明,所以属性在分配给之前是不存在的。如果分配给对象上的属性并没有创建对象属性,您将如何创建对象属性?也就是说,通过一个没有
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)