7 ms·
In "Common Mistake #2", I'd say that the mistake is fairly obvious to anyone who understands even a little bit about OOP and inheritance. Since class C doesn't
by vram22 13y ago
In "Common Mistake #2", I'd say that the mistake is fairly obvious to anyone who understands even a little bit about OOP and inheritance. Since class C doesn't define its own variable x, it has to be that it inherits the x in class A, so there's no reason to be surprised that C.x changes when A.x does.
- radiowave 13y agoWhile I agree that the "problem" case can be seen as obvious when considered in isolation, really it's the behaviour of the two cases taken together that can seem inconsistent. Nothing about understanding OOP or inheritance will prepare a person for that.
- vram22 13y ago>really it's the behaviour of the two cases taken together that can seem inconsistent. Why do you think so? I think that both cases seem consistent, or rather, correct (and therefore this example should not be treated as a common Python mistake), because x is not assigned a value anywhere in class C, and C inherits from A, so it should be clear to anyone knowing OOP and inheritance, that C's x is the same as A's x. (And the same holds true for inherited methods.) Even the OP says that in the post: >In other words, C doesn’t have its own x property, independent of A.
- radiowave 13y agoWhat's happening here is that a variable is inheriting its value from the superclass, except for when it doesn't. And when it doesn't, why is that? Well presumably it's because something's been overridden - OO tells us that's how we change the properties that are inherited from the superclass. No wait, that's not it; nothing's been overridden here. All that's happened is we've assigned a value to B.x, and doing so seems to have changed the inheritance of our class. So this variable is neither completely shared across classes and their subclasses (per Smalltalk class variables), nor completely independent across classes and their subclasses (per Smalltalk class instance variable), but instead its [in]dependence alters based upon whether (and where) you assign values to it. While I can understand that in terms of the dictionary mechanism used to implement it, from my point of view it's just weird behaviour.
- macbony 13y agoIf instead of x being an integer, it were a function x(), then it makes more sense. Really, for Python, there is no difference between the two in this example. When you assign a new value to B.x, you're overriding the value that B inherited from A. When you override the value in A, any subclass that doesn't have it's own overridden value will use the new A.x, but any subclass that is overridden will be unchanged.
- radiowave 13y agoI agree the behaviour makes sense for functions. I suppose I just prefer the idea that the meaning of assigning a value to a variable should be "assign this value to the variable", rather than "alter the inheritance behaviour of my class such that mutable state is stored in it where it wasn't stored before, and then assign this value to the variable."
- macbony 13y agox isn't a variable, it's a tag. Because Python is dynamically typed, x can be an int one minute and a function the next, so every attribute on a class is stored as a pointer to an object, including attributes that are integers (which is an object) and functions (which are also objects). Because you aren't declaring the type of x (Python doesn't allow that), Python has to treat x = 1 the same way it'd treat def x(self).
- radiowave 13y agoWhere x is an identifier (presumably aka tag?) that refers to a variable, it is not beyond the bounds of possibility for "x = 1" to be interpreted as "store the value of 1 into the variable that is referred to by identifier x". Plenty of languages, including dynamically typed ones, manage to do this, as indeed Python does in many cases. Not knowing the type of x is unrelated to question of where x's value is stored, or whether x's value will be stored somewhere else after we've assigned a new value to it.
- vram22 13y agoThis modified version of #2 might help clear things up. I've only added print statements. In general, when issues like this come up, printing the id()s of identifiers can help: Edit: Added some extra blank lines because lines were getting joined together. # class_variables.py class A(object): x = 1 class B(A): pass class C(A): pass print "Initially, A.x, B.x, C.x and their ids:" print A.x, B.x, C.x print id(A.x), id(B.x), id(C.x) B.x = 2 print "After B.x = 2, A.x, B.x, C.x and their ids:" print A.x, B.x, C.x print id(A.x), id(B.x), id(C.x) A.x = 3 print "After A.x = 3, A.x, B.x, C.x and their ids:" print A.x, B.x, C.x print id(A.x), id(B.x), id(C.x) And the output: >python class_variables.py Initially: A.x, B.x, C.x and their ids: 1 1 1 30519808 30519808 30519808 After B.x = 2: A.x, B.x, C.x and their ids: 1 2 1 30519808 30519796 30519808 After A.x = 3: A.x, B.x, C.x and their ids: 3 2 3 30519784 30519796 30519784