On Fri, Feb 28, 2014 at 11:25 PM, Mark H. Harris <harrismh...@gmail.com> wrote: > On Friday, February 28, 2014 11:16:18 PM UTC-6, Ian wrote: > >> How would you propose doing that? Bear in mind that while Python >> knows that tuples specifically are immutable, it doesn't generally >> know whether a type is immutable. > > I was going to bed, and then thought of the solution. Allow the action. > > Hold the error pending until the action either succeeds or fails for the > "item," in this case a list, and then post the error for the tuple > depending... this would make the whole mess consistent, and even compatible > with the idea of dynamic name and type binding... > > So, in other words, the action should be permitted, and actually was allowed, > and the message is incorrectly reporting something that for this particular > "item" is NOT true. The error message (and timing of same) is the bug > (philosophically). > > Under the covers it may be (and probably is) more complicated.
Indeed. For one thing, __iadd__ is not required to return the object it was called on; it could return some other object. Imagine a balanced binary tree that is stored by referencing the root node object (whether this is good design is tangential; it's a possible design). If the __iadd__ operator is implemented to insert a node, and it causes the root node to change, then it might return that new root node, expecting it to be assigned. If Python simply ignores the exception from the assignment, then the data structure has been modified in-place, but the tuple does not contain the correct object, and worst of all no exception has been raised to alert anybody of this failure condition. So at best you could only ignore these exceptions when the object returned by __iadd__ is self, which to be fair is probably most of the time. Additionally, recall my statement up-thread that "If you skip the assignment, and that assignment is meaningful to whatever the left side may be (e.g. assigning to a descriptor or something that invokes __setitem__ or __setattr__), then the operation is not equivalent." If you quash the exception from the assignment because the __iadd__ call succeeded, then you have effectively skipped the assignment and then lied to the programmer about it. For example, imagine a list subclass that contains other lists but only permits itself to be nested 1-deep; the contained lists are required to be flat. The __setitem__ method for the class might enforce the constraint and perhaps also update a count of the total combined length of the nested lists. Then suppose we have this code: lol = ListOfLists([1,2,3], [4], [6, 7, 8, 9]) lol[1] += [5, [5.25, 5.5, 5.75]] The effect of this is that the middle sublist has been extended to include a doubly-nested list. __setitem__ was then called, which noticed that the list it was passed includes another nested list, so it raised an exception instead of performing the assignment and updating the count. The ListOfLists is now in an invalid state, but if Python swallows the exception raised by __setitem__, then there is nothing to warn the programmer of this (until something else mysteriously fails to process the ListOfLists later on). The result is code that is hard to debug. Now, we might notice that the above code would probably not raise a TypeError, which is the exception we would expect if trying to mutate an immutable object, so we might revise the rule again: we only silence the exception if __iadd__ returns self and the exception is a TypeError. But even then, we have no guarantee that this is what the TypeError was meant to convey. A TypeError may have simply propagated up from other buggy code that was indirectly called by __setitem__, a legitimate error that should be brought to the programmer's attention. tldr: Swallowing exceptions is a risky practice unless you're quite sure that you know where the exception is coming from and that it's safe to ignore, and the language runtime should practically never do this. -- https://mail.python.org/mailman/listinfo/python-list