预计阅读本页时间:-
Extended Sequence Unpacking in Python 3.0
The prior section demonstrated how to use manual slicing to make sequence assignments more general. In Python 3.0 (but not 2.6), sequence assignment has been generalized to make this easier. In short, a single starred name, *X, can be used in the assignment target in order to specify a more general matching against the sequence—the starred name is assigned a list, which collects all items in the sequence not assigned to other names. This is especially handy for common coding patterns such as splitting a sequence into its “front” and “rest”, as in the preceding section’s last example.
Extended unpacking in action
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
Let’s look at an example. As we’ve seen, sequence assignments normally require exactly as many names in the target on the left as there are items in the subject on the right. We get an error if the lengths disagree (unless we manually sliced on the right, as shown in the prior section):
C:\misc> c:\python30\python
>>> seq = [1, 2, 3, 4]
>>> a, b, c, d = seq
>>> print(a, b, c, d)
1 2 3 4
>>> a, b = seq
ValueError: too many values to unpack
In Python 3.0, though, we can use a single starred name in the target to match more generally. In the following continuation of our interactive session, a matches the first item in the sequence, and b matches the rest:
>>> a, *b = seq
>>> a
1
>>> b
[2, 3, 4]
When a starred name is used, the number of items in the target on the left need not match the length of the subject sequence. In fact, the starred name can appear anywhere in the target. For instance, in the next interaction b matches the last item in the sequence, and a matches everything before the last:
>>> *a, b = seq
>>> a
[1, 2, 3]
>>> b
4
When the starred name appears in the middle, it collects everything between the other names listed. Thus, in the following interaction a and c are assigned the first and last items, and b gets everything in between them:
>>> a, *b, c = seq
>>> a
1
>>> b
[2, 3]
>>> c
4
More generally, wherever the starred name shows up, it will be assigned a list that collects every unassigned name at that position:
>>> a, b, *c = seq
>>> a
1
>>> b
2
>>> c
[3, 4]
Naturally, like normal sequence assignment, extended sequence unpacking syntax works for any sequence types, not just lists. Here it is unpacking characters in a string:
>>> a, *b = 'spam'
>>> a, b
('s', ['p', 'a', 'm'])
>>> a, *b, c = 'spam'
>>> a, b, c
('s', ['p', 'a'], 'm')
This is similar in spirit to slicing, but not exactly the same—a sequence unpacking assignment always returns a list for multiple matched items, whereas slicing returns a sequence of the same type as the object sliced:
>>> S = 'spam'
>>> S[0], S[1:] # Slices are type-specific, * assignment always returns a list
('s', 'pam')
>>> S[0], S[1:3], S[3]
('s', 'pa', 'm')
Given this extension in 3.0, as long as we’re processing a list the last example of the prior section becomes even simpler, since we don’t have to manually slice to get the first and rest of the items:
>>> L = [1, 2, 3, 4]
>>> while L:
... front, *L = L # Get first, rest without slicing
... print(front, L)
...
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
Boundary cases
Although extended sequence unpacking is flexible, some boundary cases are worth noting. First, the starred name may match just a single item, but is always assigned a list:
>>> seq
[1, 2, 3, 4]
>>> a, b, c, *d = seq
>>> print(a, b, c, d)
1 2 3 [4]
Second, if there is nothing left to match the starred name, it is assigned an empty list, regardless of where it appears. In the following, a, b, c, and d have matched every item in the sequence, but Python assigns e an empty list instead of treating this as an error case:
>>> a, b, c, d, *e = seq
>>> print(a, b, c, d, e)
1 2 3 4 []
>>> a, b, *e, c, d = seq
>>> print(a, b, c, d, e)
1 2 3 4 []
Finally, errors can still be triggered if there is more than one starred name, if there are too few values and no star (as before), and if the starred name is not itself coded inside a sequence:
>>> a, *b, c, *d = seq
SyntaxError: two starred expressions in assignment
>>> a, b = seq
ValueError: too many values to unpack
>>> *a = seq
SyntaxError: starred assignment target must be in a list or tuple
>>> *a, = seq
>>> a
[1, 2, 3, 4]
A useful convenience
Keep in mind that extended sequence unpacking assignment is just a convenience. We can usually achieve the same effects with explicit indexing and slicing (and in fact must in Python 2.X), but extended unpacking is simpler to code. The common “first, rest” splitting coding pattern, for example, can be coded either way, but slicing involves extra work:
>>> seq
[1, 2, 3, 4]
>>> a, *b = seq # First, rest
>>> a, b
(1, [2, 3, 4])
>>> a, b = seq[0], seq[1:] # First, rest: traditional
>>> a, b
(1, [2, 3, 4])
The also common “rest, last” splitting pattern can similarly be coded either way, but the new extended unpacking syntax requires noticeably fewer keystrokes:
>>> *a, b = seq # Rest, last
>>> a, b
([1, 2, 3], 4)
>>> a, b = seq[:-1], seq[-1] # Rest, last: traditional
>>> a, b
([1, 2, 3], 4)
Because it is not only simpler but, arguably, more natural, extended sequence unpacking syntax will likely become widespread in Python code over time.
Application to for loops
Because the loop variable in the for loop statement can be any assignment target, extended sequence assignment works here too. We met the for loop iteration tool briefly in Part II and will study it formally in Chapter 13. In Python 3.0, extended assignments may show up after the word for, where a simple variable name is more commonly used:
for (a, *b, c) in [(1, 2, 3, 4), (5, 6, 7, 8)]:
...
When used in this context, on each iteration Python simply assigns the next tuple of values to the tuple of names. On the first loop, for example, it’s as if we’d run the following assignment statement:
a, *b, c = (1, 2, 3, 4) # b gets [2, 3]
The names a, b, and c can be used within the loop’s code to reference the extracted components. In fact, this is really not a special case at all, but just an instance of general assignment at work. As we saw earlier in this chapter, we can do the same thing with simple tuple assignment in both Python 2.X and 3.X:
for (a, b, c) in [(1, 2, 3), (4, 5, 6)]: # a, b, c = (1, 2, 3), ...
And we can always emulate 3.0’s extended assignment behavior in 2.6 by manually slicing:
for all in [(1, 2, 3, 4), (5, 6, 7, 8)]:
a, b, c = all[0], all[1:3], all[3]
Since we haven’t learned enough to get more detailed about the syntax of for loops, we’ll return to this topic in Chapter 13.