You need to unpack N elements from an iterable, but the iterable may be longer than N elements, causing a “too many values to unpack” exception.
Python “star expressions” can be used to address this problem. For example, suppose
def drop_first_last(grades): first, *middle, last = grades return avg(middle)
As another use case, suppose you have user records that consist of a name and email
address, followed by an arbitrary number of phone numbers. You could unpack the
>>> record = ('Dave', 'email@example.com', '773-555-1212', '847-555-1212') >>> name, email, *phone_numbers = user_record >>> name 'Dave' >>> email 'firstname.lastname@example.org' >>> phone_numbers ['773-555-1212', '847-555-1212'] >>>
It’s worth noting that the phone_numbers variable will always be a list, regardless of how many phone numbers are unpacked (including none). Thus, any code that uses phone_numbers won’t have to account for the possibility that it might not be a list or perform any kind of additional type checking.
The starred variable can also be the first one in the list. For example, say you have a sequence of values representing your company’s sales figures for the last eight quarters. If you want to see how the most recent quarter stacks up to the average of the first seven, you could do something like this:
*trailing_qtrs, current_qtr = sales_record trailing_avg = sum(trailing_qtrs) / len(trailing_qtrs) return avg_comparison(trailing_avg, current_qtr)
Here’s a view of the operation from the Python interpreter:
>>> *trailing, current = [10, 8, 7, 1, 9, 5, 10, 3] >>> trailing [10, 8, 7, 1, 9, 5, 10] >>> current 3
Extended iterable unpacking is tailor-made for unpacking iterables of unknown or arbitrary length. Oftentimes, these iterables have some known component or pattern in their construction (e.g. “everything after element 1 is a phone number”), and star unpacking lets the developer leverage those patterns easily instead of performing acrobatics to get at the relevant elements in the iterable.
It is worth noting that the star syntax can be especially useful when iterating over a sequence of tuples of varying length. For example, perhaps a sequence of tagged tuples:
records = [ ('foo', 1, 2), ('bar', 'hello'), ('foo', 3, 4), ] def do_foo(x, y): print('foo', x, y) def do_bar(s): print('bar', s) for tag, *args in records: if tag == 'foo': do_foo(*args) elif tag == 'bar': do_bar(*args)
Star unpacking can also be useful when combined with certain kinds of string processing operations, such as splitting. For example:
>>> line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false' >>> uname, *fields, homedir, sh = line.split(':') >>> uname 'nobody' >>> homedir '/var/empty' >>> sh '/usr/bin/false' >>>
Sometimes you might want to unpack values and throw them away. You can’t just specify a bare * when unpacking, but you could use a common throwaway variable name, such as _ or ign (ignored). For example:
>>> record = ('ACME', 50, 123.45, (12, 18, 2012)) >>> name, *_, (*_, year) = record >>> name 'ACME' >>> year 2012 >>>
There is a certain similarity between star unpacking and list-processing features of various functional languages. For example, if you have a list, you can easily split it into head and tail components like this:
>>> items = [1, 10, 7, 4, 5, 9] >>> head, *tail = items >>> head 1 >>> tail [10, 7, 4, 5, 9] >>>
One could imagine writing functions that perform such splitting in order to carry out some kind of clever recursive algorithm. For example:
>>> def sum(items): ... head, *tail = items ... return head + sum(tail) if tail else head ... >>> sum(items) 36 >>>
However, be aware that recursion really isn’t a strong Python feature due to the inherent recursion limit. Thus, this last example might be nothing more than an academic curiosity in practice.
本文章采用 知识共享署名2.5中国大陆许可协议 进行许可，欢迎转载，演绎或用于商业目的。