python foundation 1-1: decompression sequence assignment & & asterisk decompression syntax

python cookbook Chapter 1, sections 1-2

1.1 the decompression sequence is assigned to multiple variables

Question: now there is a tuple or sequence containing N elements. How to decompress its value and assign it to N variables at the same time?

Solution any sequence (or iteratable object) can be decompressed and assigned to multiple variables through a simple assignment statement. The only premise is that the number of variables must be the same as the number of sequence elements.

Code example:

p = (4, 5)
x, y = p
x  #4
y  #5
data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
name, shares, price, date = data
name   #'ACME'
date   #(2012, 12, 21)

name, shares, price, (year, mon, day) = data
name    #'ACME'
year    #2012
mon     #12
day     #21

If the number of variables does not match the number of sequence elements, an exception will be generated.

Code example:

p = (4, 5)
x, y, z = p 

#ValueError: not enough values to unpack (expected 3, got 2)

discuss

In fact, this decompression assignment can be used on any iteratable object, not just lists or tuples. Includes strings, file objects, iterators, and generators.

Code example:

s = 'Hello'
a, b, c, d, e = s
a   #'H'
b   #'e'
e   #'o'

Sometimes, you may just want to unzip a part and discard other values. In this case, Python does not provide a special syntax. But you can use any variable name to occupy the space, and then you can just throw away these variables.

Code example:

data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
_, shares, price, _ = data
shares  #50
price   #91.1

You must ensure that the placeholder names you choose are not used elsewhere.

1.2 decompress the iteratable object and assign it to multiple variables

Problem: if the number of elements of an iteratable object exceeds the number of variables, a ValueError will be thrown. So how do I extract N elements from this iteratable object?

Solution: Python's asterisk expression can be used to solve this problem.

For example, you are studying a course. At the end of the semester, you want to count the average score of homework, but exclude the first and last scores. If there are only four scores, you may simply assign them manually, but what if there are 24? At this time, the asterisk expression comes in handy:

def drop_first_last(grades):
    first, *middle, last = grades
    return avg(middle)

In another case, suppose you now have a list of users' records. Each record contains a name, email, and then an uncertain number of phone numbers. You can break down these records as follows:

record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
name, email, *phone_numbers = record
name  #'Dave'
email  #email
phone_numbers   #['773-555-1212', '847-555-1212']

It is worth noting that the phone extracted above_ The numbers variable is always a list type, regardless of the number of phone numbers decompressed (including 0). So, any use of the phone_ The code of the numbers variable does not need to do extra type checking to confirm whether it is a list type.

The asterisk expression can also be used at the beginning of the list. For example, you have a sequence of the company's sales data for the first eight months, but you want to see the comparison between the data of the last month and the average value of the previous seven months. You can do this:

*trailing_qtrs, current_qtr = sales_record
trailing_avg = sum(trailing_qtrs) / len(trailing_qtrs)
return avg_comparison(trailing_avg, current_qtr)

The following is the result of execution in the Python interpreter:

*trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]
#Bits of all unoccupied elements
trailing     #[10, 8, 7, 1, 9, 5, 10]
current      #3

discuss

The extended iterative decompression syntax is specially designed to decompress iteratable objects with uncertain or arbitrary number of elements. Generally, the element structure of these iteratable objects has certain rules (for example, the first element is followed by a phone number). The asterisk expression allows developers to easily extract the elements by using these rules. Instead of obtaining these associated element values through some complex means.

It is worth noting that the asterisk expression is useful when iterating over sequences whose elements are variable length tuples. For example, the following is a tuple sequence with labels:

records = [      #Tuple sequence with label
    ('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)

foo 1 2
bar hello
foo 3 4

The asterisk decompression syntax is also useful in string operations, such as string segmentation.

Code 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 want to unzip some elements and discard them. You can't simply use *, but you can use a common obsolete name, such as_ Or ign ore.

Code example:

record = ('ACME', 50, 123.45, (12, 18, 2012))
name, *_, (*_, year) = record
name  #'ACME'
year  #2012

In many functional languages, the asterisk decompression syntax has many similarities with list processing. For example, if you have a list, you can easily divide it into two parts:

items = [1, 10, 7, 4, 5, 9]
head, *tail = items
head  #1
tail  #[10, 7, 4, 5, 9]

If you are smart enough, you can use this segmentation syntax to skillfully implement the recursive algorithm. For example:

def sum(items):
    head, *tail = items
    return head + sum(tail) if tail else head
sum(items)

However, due to language level constraints, recursion is not good at Python. Therefore, the last recursive demonstration is just a curious exploration. Don't take this too seriously.

Tags: Python

Posted on Sat, 23 Oct 2021 02:39:01 -0400 by mmorton