On 16 Jan 2006 14:11:25 -0800, Russell Warren <[EMAIL PROTECTED]> wrote: > I just ran across a case which seems like an odd exception to either > what I understand as the "normal" variable lookup scheme in an > instance/object heirarchy, or to the rules regarding variable usage > before creation. Check this out: > > >>> class foo(object): > ... I = 1 > ... def __init__(self): > ... print self.__dict__ > ... self.I += 1 > ... print self.__dict__ > ... > >>> a=foo() > {} > {'I': 2} > >>> foo.I > 1 > >>> a.I > 2 > >>> del a.I > >>> a.I > 1 > >>> del a.I > Traceback (most recent call last): > File "<string>", line 1, in <string> > AttributeError: I > >>> non_existent_var += 1 > Traceback (most recent call last): > File "<string>", line 1, in <string> > NameError: name 'non_existent_var' is not defined > > > In this case, 'self.I += 1' clearly has inserted a surprise > behind-the-scenes step of 'self.I = foo.I', and it is this which I find > interesting. > > As I understand it, asking for self.I at this point should check > self.__dict__ for an 'I' entry, and if it doesn't find it, head on up > to foo.__dict__ and look for it. > > So... I initially *thought* there were two possibilities for what would > happen with the 'self.I += 1': > 1. 'self.I += 1' would get a hold of 'foo.I' and increment it > 2. I'd get an AttributeError > > Both were wrong. I thought maybe an AttributeError because trying to > modify 'self.I' at that point in the code is a bit fuzzy... ie: am I > really trying to deal with foo.I (in which case, I should properly use > foo.I) or am I trying to reference an instance attribute named I (in > which case I should really create it explicitly first or get an error > as with the non_existent_var example above... maybe with 'self.I = > foo.I'). > > Python is obviously assuming the latter and is "helping" you by > automatically doing the 'self.I = foo.I' for you. Now that I know this > I (hopefully) won't make this mistake again, but doing this seems > equivalent to taking my 'non_existent_var += 1' example above and > having the interpreter interpret as "oh, you must want to deal with an > integer, so I'll just create one for you with 'non_existent_var = 0' > first". Fortunately this is not done, so why do it with the instance > attribute reference? > > Does anyone have any solid reasoning behind the Python behavior? It > might help drive it home more so than just taking it as "that's the way > it is" and remembering it. > > It gets even more confusing for me because the behaviour could be > viewed as being opposite when dealing with mutable class members. eg: > > >>> class foo(object): > ... M = [1,2,3] > ... def __init__(self): > ... self.M.append(len(self.M) + 1) > ... print self.M > ... > >>> a=foo() > [1, 2, 3, 4] > >>> foo.M > [1, 2, 3, 4] > >>> del a.M > Traceback (most recent call last): > File "<string>", line 1, in <string> > AttributeError: 'foo' object attribute 'M' is read-only > > By opposite I mean that with immutable objects, a sloppy self.I > reference doesn't get you to the base class object, whereas with a > mutable one you do get to the base object (although I do recognize that > in both cases if you just remember that the interpreter will always > stuff in a 'self.x = BaseClass.x' it works as expected in both the > immutable and mutable case). > > After all that, I guess it boils down to me thinking that the code > *should* interpret the attempted instance modification with one of the > two possibilities I mentioned above (although after typing this I'm now > leaning more towards an AttributeError rather than allowing 'self.I' to > be synonymous with 'foo.I' if no local override).
I can see how this can be confusing, but I think the confusion here is yours, not Pythons ;) self.I += 10 is an *assignment*. Like any assignment, it causes the attribute in question to be created. You wouldn't be suprised if you'd done self.I='foo' and had it "create" the instance attribute, would you? If you write out the longhand for += it becomes totally obvious what is happening and why it makes sense: temp = self.I (ends up returning value bound to foo.I) temp = temp+10 #if temp were mutable, this would modify it instead of creating a new object self.I = temp (binds name "I" in namespace "self" to the value bound to temp, which will shadow the class attribute) Note that nowhere is Python "inserting self.I = foo.I" for you - you are (implicitly) retrieving the class attribute, and then inserting that value into the instance attribute. So your case 1 is actually exactly what is happening! Python is getting a hold of foo.I and incrementing it (which you can see happening when you use a mutable object). The bit you're missing is after that, where you're then binding that value into the instance attribute. > > Russ > > PS: Apologies if I mangled the "proper" terminology for talking about > this... hopefully it makes sense. > > -- > http://mail.python.org/mailman/listinfo/python-list > -- http://mail.python.org/mailman/listinfo/python-list