
On Wed, Nov 29, 2017 at 03:08:43PM -0800, Brendan Barnwell wrote:
On 2017-11-29 14:21, Greg Ewing wrote:
On the other hand, some people seem to be interpreting the word "unpack" as in "unpack a suitcase", i.e. the suitcase is empty afterwards. But unpacking has never meant that in Python! If it did, then
x = [1, 2, 3] a, b, c = x
would leave x == [] afterwards.
The only case where unpacking behaves like that is when the rhs is an iterator rather than a sequence, in which case a side effect is unavoidable. The question then is what the side effect should be.
That's an interesting analysis, but I don't think your view is really the right one. It *is* unpacking a suitcase, it's just that *if necessary* the suitcase is constructed just in time for you to unpack it. In other words, the suitcase is not the list [1, 2, 3], but an iterator over this list.
At the point that you are conjuring from thin air an invisible suitcase that is an exact clone of the original suitcase, in order to unpack the clone without disturbing the original, I think the metaphor is so far from the real-world unpacking of suitcases that it no longer applies. Besides, it's not even correct to say that an invisible suitcase (iterator) is constructed. # Python 3.5 py> dis.dis("a, b, c = [97, 98, x]") 1 0 LOAD_CONST 0 (97) 3 LOAD_CONST 1 (98) 6 LOAD_NAME 0 (x) 9 ROT_THREE 10 ROT_TWO 11 STORE_NAME 1 (a) 14 STORE_NAME 2 (b) 17 STORE_NAME 3 (c) 20 LOAD_CONST 2 (None) 23 RETURN_VALUE Before iterators existed, Python had sequence unpacking going back to at least Python 1.5 if not older, so even if Python used a temporary and invisible iterator *now* that has not always been the case and it might not be the case in the future or in alternate interpreters. Even if Python *sometimes* builds a temporary and invisible iterator, it doesn't do it all the time, and when it does, it is pure implementation, not interface. The interpreter is free to do whatever it likes behind the scenes, but there's no iterator involved in the high-level Python statement: a, b, c = [1, 2, 3] That code involves a list and three assignment targets, that is all.
This is the same as the behavior for "for" loops: if you do "for item in [1, 2, 3]", the actual thing you're unrolling is an iterator over the list.
No, the actual thing *I* am unrolling is exactly what I write in my code, which is the list [1, 2, 3]. I don't care what the Python interpreter iterates over internally, so long as the results are the same. It can use an iterator, or unroll the loop at compile-time, or turn it into recursion over a linked list for all I care. As much as possible, we should avoid thinking about implementation details when trying to understand high-level semantics of Python code. Otherwise, our mental models become obsolete when the implementation changes. Or worse, we become incapable of thinking about better implementations (or better high-level semantics!) because the current implementation is so entwined with our understanding of the high-level semantics of the code. If we had allowed the old sequence protocol implementation to take priority over the abstract, high-level concept of iteration, we wouldn't have iterators today. -- Steve