Unpacking, Advanced Unpacking, and Nested Advanced Unpacking

Consider these expressions ... Please be patient ... this is a long list ...

(Note: some expressions are repeated - this is just to represent the "context")

a, b = 1, 2 # simple sequence assignment a, b = ['green', 'blue'] # list asqignment a, b = 'XY' # string assignment a, b = range(1,5,2) # any iterable will do # nested sequence assignment (a,b), c = "XY", "Z" # a = 'X', b = 'Y', c = 'Z' (a,b), c = "XYZ" # ERROR -- too many values to unpack (a,b), c = "XY" # ERROR -- need more than 1 value to unpack (a,b), c, = [1,2],'this' # a = '1', b = '2', c = 'this' (a,b), (c,) = [1,2],'this' # ERROR -- too many values to unpack # extended sequence unpacking a, *b = 1,2,3,4,5 # a = 1, b = [2,3,4,5] *a, b = 1,2,3,4,5 # a = [1,2,3,4], b = 5 a, *b, c = 1,2,3,4,5 # a = 1, b = [2,3,4], c = 5 a, *b = 'X' # a = 'X', b = [] *a, b = 'X' # a = [], b = 'X' a, *b, c = "XY" # a = 'X', b = [], c = 'Y' a, *b, c = "X...Y" # a = 'X', b = ['.','.','.'], c = 'Y' a, b, *c = 1,2,3 # a = 1, b = 2, c = [3] a, b, c, *d = 1,2,3 # a = 1, b = 2, c = 3, d = [] a, *b, c, *d = 1,2,3,4,5 # ERROR -- two starred expressions in assignment (a,b), c = [1,2],'this' # a = '1', b = '2', c = 'this' (a,b), *c = [1,2],'this' # a = '1', b = '2', c = ['this'] (a,b), c, *d = [1,2],'this' # a = '1', b = '2', c = 'this', d = [] (a,b), *c, d = [1,2],'this' # a = '1', b = '2', c = [], d = 'this' (a,b), (c, *d) = [1,2],'this' # a = '1', b = '2', c = 't', d = ['h', 'i', 's'] *a = 1 # ERROR -- target must be in a list or tuple *a = (1,2) # ERROR -- target must be in a list or tuple *a, = (1,2) # a = [1,2] *a, = 1 # ERROR -- 'int' object is not iterable *a, = [1] # a = [1] *a = [1] # ERROR -- target must be in a list or tuple *a, = (1,) # a = [1] *a, = (1) # ERROR -- 'int' object is not iterable *a, b = [1] # a = [], b = 1 *a, b = (1,) # a = [], b = 1 (a,b),c = 1,2,3 # ERROR -- too many values to unpack (a,b), *c = 1,2,3 # ERROR - 'int' object is not iterable (a,b), *c = 'XY', 2, 3 # a = 'X', b = 'Y', c = [2,3] # extended sequence unpacking -- NESTED (a,b),c = 1,2,3 # ERROR -- too many values to unpack *(a,b), c = 1,2,3 # a = 1, b = 2, c = 3 *(a,b) = 1,2 # ERROR -- target must be in a list or tuple *(a,b), = 1,2 # a = 1, b = 2 *(a,b) = 'XY' # ERROR -- target must be in a list or tuple *(a,b), = 'XY' # a = 'X', b = 'Y' *(a, b) = 'this' # ERROR -- target must be in a list or tuple *(a, b), = 'this' # ERROR -- too many values to unpack *(a, *b), = 'this' # a = 't', b = ['h', 'i', 's'] *(a, *b), c = 'this' # a = 't', b = ['h', 'i'], c = 's' *(a,*b), = 1,2,3,3,4,5,6,7 # a = 1, b = [2, 3, 3, 4, 5, 6, 7] *(a,*b), *c = 1,2,3,3,4,5,6,7 # ERROR -- two starred expressions in assignment *(a,*b), (*c,) = 1,2,3,3,4,5,6,7 # ERROR -- 'int' object is not iterable *(a,*b), c = 1,2,3,3,4,5,6,7 # a = 1, b = [2, 3, 3, 4, 5, 6], c = 7 *(a,*b), (*c,) = 1,2,3,4,5,'XY' # a = 1, b = [2, 3, 4, 5], c = ['X', 'Y'] *(a,*b), c, d = 1,2,3,3,4,5,6,7 # a = 1, b = [2, 3, 3, 4, 5], c = 6, d = 7 *(a,*b), (c, d) = 1,2,3,3,4,5,6,7 # ERROR -- 'int' object is not iterable *(a,*b), (*c, d) = 1,2,3,3,4,5,6,7 # ERROR -- 'int' object is not iterable *(a,*b), *(c, d) = 1,2,3,3,4,5,6,7 # ERROR -- two starred expressions in assignment *(a,b), c = 'XY', 3 # ERROR -- need more than 1 value to unpack *(*a,b), c = 'XY', 3 # a = [], b = 'XY', c = 3 (a,b), c = 'XY', 3 # a = 'X', b = 'Y', c = 3 *(a,b), c = 'XY', 3, 4 # a = 'XY', b = 3, c = 4 *(*a,b), c = 'XY', 3, 4 # a = ['XY'], b = 3, c = 4 (a,b), c = 'XY', 3, 4 # ERROR -- too many values to unpack 

How do you understand such complexity and confusion. As always, you can CORRECTLY when calculating the results of such expressions manually. Or, while reading some other code, should I just ignore them and never try to figure out what the expression actually does?

+58
python
Aug 6 2018-11-11T00:
source share
3 answers

My apologies for the length of this post, but I decided to choose the completeness.

Once you know a few basic rules, they are not difficult to generalize. I will do my best to explain a few examples. Since you are talking about evaluating these “manually,” I will offer some simple replacement rules. Basically, it might be easier for you to understand the expression if all iterations are formatted in the same way.

Only for unpacking on the right side of = (i.e. for rvalues) the following substitutions are valid:

 'XY' -> ('X', 'Y') ['X', 'Y'] -> ('X', 'Y') 

If you find that the value is not decompressed, you will cancel the replacement. (See below for more details).

Also, when you see bare commas, pretend to be a top-level tuple there. Do this on both the left and right sides (i.e. for lvalues ​​and rvalues):

 'X', 'Y' -> ('X', 'Y') a, b -> (a, b) 

Given these simple rules, here are a few examples:

 (a,b), c = "XY", "Z" # a = 'X', b = 'Y', c = 'Z' 

Applying the above rules, we convert "XY" to ('X', 'Y') and cover the bare commas in parens:

 ((a, b), c) = (('X', 'Y'), 'Z') 

The visual correspondence here makes it pretty obvious how the assignment works.

Here is an erroneous example:

 (a,b), c = "XYZ" 

Following the above replacement rules, we get the following:

 ((a, b), c) = ('X', 'Y', 'Z') 

This is clearly a mistake; nested structures do not match. Now let's see how this works for a slightly more complex example:

 (a,b), c, = [1,2],'this' # a = '1', b = '2', c = 'this' 

Applying the above rules, we get

 ((a, b), c) = ((1, 2), ('t', 'h', 'i', 's')) 

But now it’s clear from the structure that 'this' not unpacked, but directly assigned c . Therefore, we are canceling the replacement.

 ((a, b), c) = ((1, 2), 'this') 

Now let's see what happens when we complete c in the tuple:

 (a,b), (c,) = [1,2],'this' # ERROR -- too many values to unpack 

becomes

 ((a, b), (c,)) = ((1, 2), ('t', 'h', 'i', 's')) 

Again, the error is obvious. c no longer a bare variable, but a variable inside the sequence, so the corresponding sequence on the right is unpacked in (c,) . But the sequences have different lengths, so there is an error.

Now for extended unpacking using the * operator. This is a little trickier, but still pretty simple. The variable preceding * becomes a list that contains any elements from the corresponding sequence that are not assigned to variable names. Starting with a pretty simple example:

 a, *b, c = "X...Y" # a = 'X', b = ['.','.','.'], c = 'Y' 

It is getting

 (a, *b, c) = ('X', '.', '.', '.', 'Y') 

The easiest way to analyze this is to work from the end. 'X' assigned a , and 'Y' assigned c . The remaining values ​​in the sequence are put into the list and assigned b .

Lvalues ​​such as (*a, b) and (a, *b) are just the special cases above. You cannot have two * statements inside the same lvalue sequence, because it will be ambiguous. Where the values ​​will look something like this: (a, *b, *c, d) - in b or c ? I will consider the enclosed case in an instant.

 *a = 1 # ERROR -- target must be in a list or tuple 

Here the error is pretty clear. The target ( *a ) must be in the tuple.

 *a, = (1,2) # a = [1,2] 

This works because there is a bare comma. Applying rules ...

 (*a,) = (1, 2) 

Since there are no variables except *a , *a pulls out all the values ​​in the rvalue sequence. What if you replace (1, 2) with one value?

 *a, = 1 # ERROR -- 'int' object is not iterable 

becomes

 (*a,) = 1 

Again, the error here is self-explanatory. You cannot unpack what is not a sequence, but *a need to unpack something. Therefore we put it in sequence

 *a, = [1] # a = [1] 

Which is equivalent

 (*a,) = (1,) 

Finally, this is a common point of embarrassment: (1) same as 1 - you need a comma to distinguish a tuple from an arithmetic statement.

 *a, = (1) # ERROR -- 'int' object is not 

Now for nesting. Actually this example was not in your "NESTED" section; maybe you didn’t understand that it was nested?

 (a,b), *c = 'XY', 2, 3 # a = 'X', b = 'Y', c = [2,3] 

becomes

 ((a, b), *c) = (('X', 'Y'), 2, 3) 

The first value in the top-level court is assigned, and the remaining values ​​in the top-level court ( 2 and 3 ) are assigned c - as you would expect.

 (a,b),c = 1,2,3 # ERROR -- too many values to unpack *(a,b), c = 1,2,3 # a = 1, b = 2, c = 3 

I already explained why the first line causes an error. The second line is stupid, but here's why it works:

 (*(a, b), c) = (1, 2, 3) 

As explained earlier, we work with the ends. 3 , c assigned, and then the remaining values ​​are assigned to the variable with the preceding * , in this case (a, b) . Thus, which is equivalent to (a, b) = (1, 2) , which works because there is the correct number of elements. I can’t think of any reason that has ever appeared in working code. Similarly

 *(a, *b), c = 'this' # a = 't', b = ['h', 'i'], c = 's' 

becomes

 (*(a, *b), c) = ('t', 'h', 'i', 's') 

Work with ends, 's' is assigned to c , and ('t', 'h', 'i') is assigned (a, *b) . Working again from the end, 't' is assigned a , and ('h', 'i') is assigned b as a list. This is another stupid example that should never appear in working code.

+61
Aug 6 '11 at 17:30
source share

I find the Python 2 tuple set is pretty simple. Each name on the left corresponds either to an entire sequence or to one element in the sequence to the right. If the names correspond to the individual elements of any sequence, then there should be enough names to cover all the elements.

Extended unpacking, of course, can be confusing because it is so powerful. The reality is that you should never do the last 10 or more valid examples that you gave - if the data is structured, it should be in a dict or class instance, and not unstructured forms like lists.

Obviously, the new syntax can be abused. The answer to your question is that you do not need to read such expressions - this is bad practice, and I doubt that they will be used.

Just because you can write arbitrarily complex expressions doesn't mean you should. You can write code like map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables)) , but you are not .

+5
Aug 6 2018-11-11T00:
source share

I think your code might be misleading to use it in another form.

This is similar to using extra parentheses in expressions to avoid questions about operator precedence. I have always been a good investment to make your code readable.

I prefer to use unpacking only for simple tasks like swap.

+2
Aug 6 '11 at 15:08
source share



All Articles