On Mon, Dec 16, 2013 at 2:30 PM, liuerfire Wang <liuerf...@gmail.com> 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! ChrisA -- https://mail.python.org/mailman/listinfo/python-list