On Monday, December 16, 2013 9:27:11 AM UTC+5:30, Chris Angelico wrote: > On Mon, Dec 16, 2013 at 2:30 PM, liuerfire Wang wrote: > > TypeError: 'tuple' object does not support item assignment > > In [5]: a > > Out[5]: ([1, 1], []) > > no problem, there is an exception. But a is still changed. > > is this a bug, or could anyone explain it?
> It's not a bug, but it's a bit confusing. Here's what happens. The > augmented assignment operator += triggers an in-place addition (where > possible; for lists, it's possible), so the original list will be > changed: > >>> a = [1] > >>> b = a > >>> a += [2] > >>> b > [1, 2] > Whereas using separate assignment and addition creates a new list: > >>> a = a + [3] > >>> a > [1, 2, 3] > >>> b > [1, 2] > To handle both mutable types (like list) and immutable ones (like > str), the augmented assignment operators have to be able to choose > whether or not they change their object: > >>> a = "1" > >>> b = a > >>> a += "2" > >>> b > '1' > >>> a > '12' > So what happens under the hood is, more or less: > a += [2] > # ... is equivalent to ... > a = a.__iadd__([2]) > Which can be explored interactively: > >>> a = [1] > >>> a.__iadd__([2]) > [1, 2] > >>> a > [1, 2] > The __iadd__ function ("in-place add") returns the new result, and in > the case of the list, that's done by changing the list. The assignment > is then done, which does nothing here, but with strings, is the > critical part of the job. So far, so good. > Now, when that result gets assigned to the tuple, we have a problem. > Tuples are immutable - can't change length (no appending), and can't > assign to elements. So when the final step happens, an exception is > thrown. At that point, the tuple is complaining "Hey, you can't assign > to me!", but the list has already done the appending, and it's too > late to undo that. So the list has changed, and when you go look at > it, you see the change (since the list is the same whether you find it > from the tuple or somewhere else), which creates the confusing > situation of throwing an exception after doing exactly what you > wanted. > Python exceptions aren't like SQL errors, causing a rollback of the > whole transaction. Or, more generally, Python expressions and > statements aren't transactional. So it's entirely possible, if a > little confusing, for part of a job to happen. Let's take another > example that has side effects: > tup = (1,2,3) > tup[2] = input("Enter something: ") # use raw_input() in Python 2 > Tuple assignment can't work... but it's fairly clear that this > _should_ still show the prompt and wait for the user to type > something, and only throw the exception later on. It's not as obvious > in the augmented assignment example, but both are actually doing the > same thing. > Hope that helps! Heh! Nice question... even nicer explanation!! I thought I knew that imperative programming is a bad idea: http://blog.languager.org/2012/11/imperative-programming-lessons-not.html Never cease to be surprised how bad an idea it is! -- https://mail.python.org/mailman/listinfo/python-list