On Sat, 05 Nov 2005 14:37:19 +1100, Steven D'Aprano <[EMAIL PROTECTED]> wrote:
>On Sat, 05 Nov 2005 00:25:34 +0000, Bengt Richter wrote: > >> On Fri, 04 Nov 2005 02:59:35 +1100, Steven D'Aprano <[EMAIL PROTECTED]> >> wrote: >> >>>On Thu, 03 Nov 2005 14:13:13 +0000, Antoon Pardon wrote: >>> >>>> Fine, we have the code: >>>> >>>> b.a += 2 >>>> >>>> We found the class variable, because there is no instance variable, >>>> then why is the class variable not incremented by two now? >> Because the class variable doesn't define a self-mutating __iadd__ >> (which is because it's an immutable int, of course). If you want >> b.__dict__['a'] += 2 or b.__class__.__dict__['a'] += 2 you can >> always write it that way ;-) >> >> (Of course, you can use a descriptor to define pretty much whatever semantics >> you want, when it comes to attributes). >> >>> >>>Because b.a += 2 expands to b.a = b.a + 2. Why would you want b.a = >> >> No, it doesn't expand like that. (Although, BTW, a custom import could >> make it so by transforming the AST before compiling it ;-) >> >> Note BINARY_ADD is not INPLACE_ADD: > >Think about *what* b.a += 2 does, not *how* it does it. Perhaps for some what it does, or what in the abstract it was intended to do? (which we need BDFL channeling to know for sure ;-) It looks like it means, "add two to <whatever b.a is>". I think Antoon is unhappy that <whatever b.a is> is not determined once for the one b.a expression in the statement. I sympathize, though it's a matter of defining what b.a += 2 is really intended to mean. The parses are certainly distinguishable: >>> import compiler >>> compiler.parse('b.a +=2','exec').node Stmt([AugAssign(Getattr(Name('b'), 'a'), '+=', Const(2))]) >>> compiler.parse('b.a = b.a + 2','exec').node Stmt([Assign([AssAttr(Name('b'), 'a', 'OP_ASSIGN')], Add((Getattr(Name('b'), 'a'), Const(2))))]) Which I think leads to the different (BINARY_ADD vs INPLACE_ADD) code, which probably really ought to have a conditional STORE_ATTR for the result of INPLACE_ADD, so that if __iadd__ was defined, it would be assumed that the object took care of everything (normally mutating itself) and no STORE_ATTR should be done. But that's not the way it works now. (See also my reply to Mike). Perhaps all types that want to be usable with inplace ops ought to inherit from some base providing that, and there should never be a return value. This would be tricky for immutables though, since re-binding is necessary, and the __iadd__ method would have to be passed the necessary binding context and methods. Probably too much of a rewrite to be practical. >other data type it would make a difference whether the mechanism was >BINARY_ADD (__add__) or INPLACE_ADD (__iadd__), but in this case it does >not. Both of them do the same thing. Unfortunately you seem to be right in this case. > >Actually, no "perhaps" about it -- we've already discussed the case of >lists. Well, custom objects have to be considered too. And where attribute access is involved, descriptors. > >Sometimes implementation makes a difference. I assume BINARY_ADD and >INPLACE_ADD work significantly differently for lists, because their >results are significantly (but subtly) different: > >py> L = [1,2,3]; id(L) >-151501076 >py> L += [4,5]; id(L) >-151501076 >py> L = L + []; id(L) >-151501428 > Yes. > >But all of this is irrelevant to the discussion about binding b.a >differently on the left and right sides of the equals sign. We have >discussed that the behaviour is different with mutable objects, because >they are mutable -- if I recall correctly, I was the first one in this >thread to bring up the different behaviour when you append to a list >rather than reassign, that is, modify the class attribute in place. > >I'll admit that my choice of terminology was not the best, but it wasn't >misleading. b.a += 2 can not modify ints in place, and so the >effect of b.a += 2 is the same as b.a = b.a + 2, regardless of what >byte-codes are used, or even what C code eventually implements that >add-and-store. It is so currently, but that doesn't mean that it couldn't be otherwise. I think there is some sense to the idea that b.a should be re-bound in the same namespace where it was found with the single apparent evaluation of "b.a" in "b.a += 2" (which incidentally is Antoon's point, I think). This is just for augassign, of course. OTOH, this would be find-and-rebind logic for attributes when augassigned, and that would enable some tricky name-collision bugs for typos, and code that used instance.attr += incr depending on current behavior would break. > >In the case of lists, setting Class.a = [] and then calling instance.a += >[1] would not exhibit the behaviour Antoon does not like, because the >addition is done in place. But calling instance.a = instance.a + [1] >would. > >My question still stands: why would you want instance.a = <something> >to operate as instance.__class__.a = <something>? > Because in the case of instance.a += <increment>, "instance.a" is a short spelling for "instance.__class__.a" (in the limited case we are discussing), and that spelling specifies _both_ source and target in a _single_ expression, unlike instance.a = instance.a + <incr> where two expressions are used, which one should expect to have their meaning accoring to the dynamic moment and context of their evaluation. If 'a' in vars(instance) then instance.a has the meaning instance.__dict__['a'] for both source and target of +=. I think you can argue for the status quo or find-and-rebind, but since there are adequate workarounds to let you do what you want, I don't expect a change. I do think that returning NotImplemented from __iadd__ to indicate no binding of return value desired (as opposed to __iadd__ itself not implemented, which is detected before the call) might make things more controllable for custom objects. Sorry about cramming too much into sentences ;-/ Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list