Unpacking Elements from Iterables of Arbitrary Length

Unpacking Elements from Iterables of Arbitrary Length

Problem:

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.

Solution:

Python “star expressions” can be used to address this problem. For example, suppose

you run a course and decide at the end of the semester that you’re going to drop the first

and last homework grades, and only average the rest of them. If there are only four

assignments, maybe you simply unpack all four, but what if there are 24? A star expression

makes it easy:

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

records like this:

>>> record = ('Dave', '[email protected]', '773-555-1212', '847-555-1212')

>>> name, email, *phone_numbers = record

>>> name

'Dave'

>>> email

'[email protected]'

>>> 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        

Discussion:

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.

----------------------------------

For more info about 'Handling Recursion Limit' and setting the maximum depth of the Python interpreter stack refer to the followings:

Handling Recursion Limit

How to set the maximum depth of the Python interpreter stack

*** Learned from:'Python Cookbook' & 'https://www.geeksforgeeks.org'***        

要查看或添加评论,请登录

Mojtaba Ahmadpour的更多文章

  • Role of Underscore(_) in Python

    Role of Underscore(_) in Python

    Many of the Python Developers don't know about the functionalities of underscore(_) in Python. It helps users to write…

  • The Meaning of Underscores (_) in Python

    The Meaning of Underscores (_) in Python

    The various meanings and naming conventions around single and double underscores (“dunder”) in Python, how name…

  • The Zen of Python

    The Zen of Python

    Perhaps the best known collection of Python philosophy was written by Tim Peters. Zen of Python condenses some of the…

  • Keeping the Last N Items

    Keeping the Last N Items

    Problem: You want to keep a limited history of the last few items seen during iteration or during some other kind of…

  • Unpacking a Sequence into Separate Variables

    Unpacking a Sequence into Separate Variables

    Problem: You have an N-element tuple or sequence that you would like to unpack into a collection of N variables…

社区洞察