msg239714 - (view) |
Author: Jethro (jethro) |
Date: 2015-03-31 14:01 |
Look at this simple code: class A: tot = 0 def __init__(self, a): self.tot += a x = A(3) print(x.tot, A.tot) Result from print: 3 0 What the interpreter did was that it first resolved self.tot to be the class field tot, which resolves to 0, then it created an instance field tot to hold the result 3. This is not correct, as one single self.tot meant two different things. Two ways to fix this: 1. report a name undefined error (interpret self.tot as instance field) 2. increase A.tot (interpret self.tot as class field) Clearly 1 seems more naturally to Python, even though I wished python could disallow a class field to be shadowed by instance field, which seems quite a reasonable thing to do. |
|
|
msg239715 - (view) |
Author: R. David Murray (r.david.murray) *  |
Date: 2015-03-31 14:11 |
Yep, this is the way it works. When a class attribute name is referenced on an instance object, you are referencing the object pointed to by the class name. What happens next depends on what kind of object you have, and what kind of operation you perform. In this case, you have an immutable object, and the addition operation returns a new immutable object, which gets assigned (as always happens) to the instance attribute name. If tot was, say, [0] and the operation was self.tot += [1], your print would print [0, 1] [0, 1], because the in-place addition operation on lists *mutatates* the list, and what gets assigned to the instance attribute name is a pointer to the *same* object that the class attribute points to. In short, this is a more subtle instance of the confusion discussed here: https://docs.python.org/3/faq/programming.html#why-did-changing-list-y-also-change-list-x (and also the FAQ that follows that one). |
|
|
msg239717 - (view) |
Author: Jethro (jethro) |
Date: 2015-03-31 14:39 |
I believe Mr. Murray somehow missed the point. My point is that the very same self.tot is interpreted as two different things: instance field and class field. In another analogous case, the interpreter would be more consistent: >>> tot = 0 >>> def addtot(x): tot+=x >>> addtot(3) If this tot is allowed to be resolved to different things, there should be no problem. Instead you get an error: Traceback (most recent call last): File "<pyshell#106>", line 1, in addtot(3) File "<pyshell#105>", line 1, in addtot def addtot(x): tot += x UnboundLocalError: local variable 'tot' referenced before assignment |
|
|
msg239721 - (view) |
Author: R. David Murray (r.david.murray) *  |
Date: 2015-03-31 14:57 |
Ah, now I understand your confusion. Class variables are special. The first time you reference a name on an instance that is not currently defined on that instance but is defined on the class, the interpreter gets the object pointer from the class reference. It then performs the operation, and assigns the result to the *instance* attribute. This is how class attributes work, and is an integral part of Python. This is documented in the 'class instances' section of https://docs.python.org/3/reference/datamodel.html#objects-values-and-types. |
|
|
msg239766 - (view) |
Author: Jethro (jethro) |
Date: 2015-04-01 00:01 |
I'm not confused. In my first report, I have already explained why python behaves that way. My point is, this is not the right way to go. Because the very same self.tot in the in-place operation self.tot+=a is first resolved to be the class field, then an instance field. This is against any sensible design of a language. If this is OK, then my second example should also be OK. In my second example, tot += x, the very same tot can be first resolved to the global variable tot, then the result is assigned to a local variable. |
|
|
msg239808 - (view) |
Author: R. David Murray (r.david.murray) *  |
Date: 2015-04-01 13:40 |
And I'm telling you that *this is how Python is designed*, and it is not going to change. Class attributes are *special*, for a reason. Please do not reopen this issue again. If you wish to pursue this further, the language design discussion forum is the python-ideas mailing list. Guido participates in that list; so, you can talk directly to the language designer about this if you wish. You aren't going to get a very warm reception by saying it is broken, though, because there is lots and lots of Python code that makes use of how class attributes work. And like I said they are special, so suggesting that the way other scopes work be changed is also pretty much a non-starter, I'm pretty sure. |
|
|