<jupyter_start><jupyter_text>Chapter 2 — An Array of Sequences**Sections with code snippets in this chapter:*** [List Comprehensions and Generator Expressions](List-Comprehensions-and-Generator-Expressions)* [Tuples Are Not Just Immutable Lists](Tuples-Are-Not-Just-Immutable-Lists)* [Unpacking sequences and iterables](Unpacking-sequences-and-iterables)* [Pattern Matching with Sequences](Pattern-Matching-with-Sequences)* [Slicing](Slicing)* [Using + and * with Sequences](Using-+-and-*-with-Sequences)* [Augmented Assignment with Sequences](Augmented-Assignment-with-Sequences)* [list.sort and the sorted Built-In Function](list.sort-and-the-sorted-Built-In-Function)* [When a List Is Not the Answer](When-a-List-Is-Not-the-Answer)* [Memory Views](Memory-Views)* [NumPy and SciPy](NumPy-and-SciPy)* [Deques and Other Queues](Deques-and-Other-Queues)* [Soapbox](Soapbox)  List Comprehensions and Generator Expressions  Example 2-1. Build a list of Unicode codepoints from a string<jupyter_code>symbols = '$¢£¥€¤'
codes = []

for symbol in symbols:
    codes.append(ord(symbol))

codes<jupyter_output><empty_output><jupyter_text>Example 2-2. Build a list of Unicode codepoints from a string, using a listcomp<jupyter_code>symbols = '$¢£¥€¤'

codes = [ord(symbol) for symbol in symbols]

codes<jupyter_output><empty_output><jupyter_text>Box: Listcomps No Longer Leak Their Variables<jupyter_code>x = 'ABC'
codes = [ord(x) for x in x]
x
codes
codes = [last := ord(c) for c in x]
last<jupyter_output><empty_output><jupyter_text>Example 2-3. The same list built by a listcomp and a map/filter composition<jupyter_code>symbols = '$¢£¥€¤'
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
beyond_ascii
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))
beyond_ascii<jupyter_output><empty_output><jupyter_text>Example 2-4. Cartesian product using a list comprehension<jupyter_code>colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors for size in sizes]
tshirts
for color in colors:
    for size in sizes:
        print((color, size))
shirts = [(color, size) for size in sizes
          for color in colors]
tshirts<jupyter_output><empty_output><jupyter_text>Example 2-5. Initializing a tuple and an array from a generator expression<jupyter_code>symbols = '$¢£¥€¤'
tuple(ord(symbol) for symbol in symbols)
import array

array.array('I', (ord(symbol) for symbol in symbols))<jupyter_output><empty_output><jupyter_text>Example 2-6. Cartesian product in a generator expression<jupyter_code>colors = ['black', 'white']
sizes = ['S', 'M', 'L']

for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):
    print(tshirt)<jupyter_output>black S
black M
black L
white S
white M
white L<jupyter_text>Tuples Are Not Just Immutable Lists  Example 2-7. Tuples used as records<jupyter_code>lax_coordinates = (33.9425, -118.408056)
city, year, pop, chg, area = ('Tokyo', 2003, 32_450, 0.66, 8014)
traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]

for passport in sorted(traveler_ids):
    print('%s/%s' % passport)
for country, _ in traveler_ids:
    print(country)<jupyter_output>USA
BRA
ESP<jupyter_text>Tuples as Immutable Lists<jupyter_code>a = (10, 'alpha', [1, 2])
b = (10, 'alpha', [1, 2])
a == b
b[-1].append(99)
a == b
b
def fixed(o):
    try:
        hash(o)
    except TypeError:
        return False
    return True


tf = (10, 'alpha', (1, 2))  # Contains no mutable items
tm = (10, 'alpha', [1, 2])  # Contains a mutable item (list)
fixed(tf)
fixed(tm)<jupyter_output><empty_output><jupyter_text>Unpacking sequences and iterables<jupyter_code>lax_coordinates = (33.9425, -118.408056)
latitude, longitude = lax_coordinates  # unpacking
latitude
longitude
divmod(20, 8)
t = (20, 8)
divmod(*t)
quotient, remainder = divmod(*t)
quotient, remainder
import os

_, filename = os.path.split('/home/luciano/.ssh/id_rsa.pub')
filename<jupyter_output><empty_output><jupyter_text>Using * to grab excess items<jupyter_code>a, b, *rest = range(5)
a, b, rest
a, b, *rest = range(3)
a, b, rest
a, b, *rest = range(2)
a, b, rest
a, *body, c, d = range(5)
a, body, c, d
*head, b, c, d = range(5)
head, b, c, d<jupyter_output><empty_output><jupyter_text>Unpacking with * in function calls and sequence literals<jupyter_code>def fun(a, b, c, d, *rest):
    return a, b, c, d, rest


fun(*[1, 2], 3, *range(4, 7))
*range(4), 4
[*range(4), 4]
{*range(4), 4, *(5, 6, 7)}<jupyter_output><empty_output><jupyter_text>Nested unpacking Example 2-8. Unpacking nested tuples to access the longitude[02-array-seq/metro_lat_lon.py](02-array-seq/metro_lat_lon.py)  Pattern Matching with Sequences Example 2-9. Method from an imaginary Robot class<jupyter_code># def handle_command(self, message):
#     match message:
#         case ['BEEPER', frequency, times]:
#             self.beep(times, frequency)
#         case ['NECK', angle]:
#             self.rotate_neck(angle)
#         case ['LED', ident, intensity]:
#             self.leds[ident].set_brightness(ident, intensity)
#         case ['LED', ident, red, green, blue]:
#             self.leds[ident].set_color(ident, red, green, blue)
#         case _:
#             raise InvalidCommand(message)<jupyter_output><empty_output><jupyter_text>Example 2-10. Destructuring nested tuples—requires Python ≥ 3.10.[02-array-seq/match_lat_lon.py](02-array-seq/match_lat_lon.py)<jupyter_code>metro_areas = [
    ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
    ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
    ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
    ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
    ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]

def main():
    print(f'{"":15} | {"latitude":>9} | {"longitude":>9}')
    for record in metro_areas:
        match record:
            case [name, _, _, (lat, lon)] if lon <= 0:
                print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')
main()<jupyter_output>|  latitude | longitude
Mexico City     |   19.4333 |  -99.1333
New York-Newark |   40.8086 |  -74.0204
São Paulo       |  -23.5478 |  -46.6358<jupyter_text>Pattern Matching Sequences in an Interpreter Example 2-11. Matching patterns without match/case.[02-array-seq/lispy/py3.9/lis.py](02-array-seq/lispy/py3.9/lis.py)  Example 2-12. Pattern matching with match/case—requires Python ≥ 3.10.[02-array-seq/lispy/py3.10/lis.py](02-array-seq/lispy/py3.10/lis.py)  Slicing  Why Slices and Range Exclude the Last Item<jupyter_code>l = [10, 20, 30, 40, 50, 60]

l[:2]  # split at 2
l[2:]
l[:3]  # split at 3
l[3:]<jupyter_output><empty_output><jupyter_text>Slice Objects<jupyter_code>s = 'bicycle'
s[::3]
s[::-1]
s[::-2]<jupyter_output><empty_output><jupyter_text>Example 2-13. Line items from a flat-file invoice<jupyter_code>invoice = """
0.....6.................................40........52...55........
1909 Pimoroni PiBrella                      $17.50    3    $52.50
1489 6mm Tactile Switch x20                  $4.95    2    $9.90
1510 Panavise Jr. - PV-201                  $28.00    1    $28.00
1601 PiTFT Mini Kit 320x240                 $34.95    1    $34.95
"""

SKU = slice(0, 6)
DESCRIPTION = slice(6, 40)
UNIT_PRICE = slice(40, 52)
QUANTITY = slice(52, 55)
ITEM_TOTAL = slice(55, None)

line_items = invoice.split('\n')[2:]

for item in line_items:
    print(item[UNIT_PRICE], item[DESCRIPTION])<jupyter_output>$17.50   imoroni PiBrella                  
     $4.95   mm Tactile Switch x20             
    $28.00   anavise Jr. - PV-201              
    $34.95   iTFT Mini Kit 320x240<jupyter_text>Assigning to Slices<jupyter_code>l = list(range(10))
l
l[2:5] = [20, 30]
l
del l[5:7]
l
l[3::2] = [11, 22]
l<jupyter_output><empty_output><jupyter_text>By design, this example raises an exception::<jupyter_code>try:
    l[2:5] = 100
except TypeError as e:
    print(repr(e))
l[2:5] = [100]
l<jupyter_output><empty_output><jupyter_text>Using + and * with Sequences<jupyter_code>l = [1, 2, 3]
l * 5
5 * 'abcd'<jupyter_output><empty_output><jupyter_text>Building Lists of Lists  Example 2-14. A list with three lists of length 3 can represent a tic-tac-toe board<jupyter_code>board = [['_'] * 3 for i in range(3)]
board
board[1][2] = 'X'
board<jupyter_output><empty_output><jupyter_text>Example 2-15. A list with three references to the same list is useless<jupyter_code>weird_board = [['_'] * 3] * 3
weird_board
weird_board[1][2] = 'O'
weird_board<jupyter_output><empty_output><jupyter_text>Explanation<jupyter_code>board = []
for i in range(3):
    row = ['_'] * 3
    board.append(row)
board
board[2][0] = 'X'
board<jupyter_output><empty_output><jupyter_text>Augmented Assignment with Sequences<jupyter_code>l = [1, 2, 3]
idl = id(l)
# NBVAL_IGNORE_OUTPUT
idl
l *= 2
l
id(l) == idl  # same list
t = (1, 2, 3)
idt = id(t)
# NBVAL_IGNORE_OUTPUT
idt
t *= 2
id(t) == idt  # new tuple<jupyter_output><empty_output><jupyter_text>A += Assignment Puzzler Example 2-16. A riddle<jupyter_code>t = (1, 2, [30, 40])
try:
    t[2] += [50, 60]
except TypeError as e:
    print(repr(e))<jupyter_output>TypeError("'tuple' object does not support item assignment")<jupyter_text>Example 2-17. The unexpected result: item t2 is changed and an exception is raised<jupyter_code>t<jupyter_output><empty_output><jupyter_text>Example 2-18. Bytecode for the expression s[a] += b<jupyter_code>import dis

dis.dis('s[a] += b')<jupyter_output>1           0 LOAD_NAME                0 (s)
              2 LOAD_NAME                1 (a)
              4 DUP_TOP_TWO
              6 BINARY_SUBSCR
              8 LOAD_NAME                2 (b)
             10 INPLACE_ADD
             12 ROT_THREE
             14 STORE_SUBSCR
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE<jupyter_text>list.sort and the sorted Built-In Function<jupyter_code>fruits = ['grape', 'raspberry', 'apple', 'banana']
sorted(fruits)
fruits
sorted(fruits, reverse=True)
sorted(fruits, key=len)
sorted(fruits, key=len, reverse=True)
fruits
fruits.sort()
fruits<jupyter_output><empty_output><jupyter_text>When a List Is Not the Answer  Arrays  Example 2-19. Creating, saving, and loading a large array of floats<jupyter_code>from array import array
from random import random, seed
seed(10)  # Use seed to make the output consistent

floats = array('d', (random() for i in range(10 ** 7)))
floats[-1]
with open('floats.bin', 'wb') as fp:
    floats.tofile(fp)
floats2 = array('d')

with open('floats.bin', 'rb') as fp:
    floats2.fromfile(fp, 10 ** 7)

floats2[-1]
floats2 == floats<jupyter_output><empty_output><jupyter_text>Memory Views  Example 2-20. Handling 6 bytes memory of as 1×6, 2×3, and 3×2 views<jupyter_code>octets = array('B', range(6))
m1 = memoryview(octets)
m1.tolist()
m2 = m1.cast('B', [2, 3])
m2.tolist()
m3 = m1.cast('B', [3, 2])
m3.tolist()
m2[1,1] = 22
m3[1,1] = 33
octets<jupyter_output><empty_output><jupyter_text>Example 2-21. Changing the value of an 16-bit integer array item by poking one of its bytes<jupyter_code>numbers = array('h', [-2, -1, 0, 1, 2])
memv = memoryview(numbers)
len(memv)
memv[0]
memv_oct = memv.cast('B')
memv_oct.tolist()
memv_oct[5] = 4
numbers<jupyter_output><empty_output><jupyter_text>NumPy  Example 2-22. Basic operations with rows and columns in a numpy.ndarray<jupyter_code>import numpy as np

a = np.arange(12)
a
type(a)
a.shape
a.shape = 3, 4
a
a[2]
a[2, 1]
a[:, 1]
a.transpose()<jupyter_output><empty_output><jupyter_text>Example 2-22. Loading, saving, and vectorized operations<jupyter_code>with open('floats-1M-lines.txt', 'wt') as fp:
    for _ in range(1_000_000):
        fp.write(f'{random()}\n')
floats = np.loadtxt('floats-1M-lines.txt')
floats[-3:]
floats *= .5
floats[-3:]
from time import perf_counter as pc

t0 = pc()
floats /= 3
(pc() - t0) < 0.01
np.save('floats-1M', floats)
floats2 = np.load('floats-1M.npy', 'r+')
floats2 *= 6
floats2[-3:]<jupyter_output><empty_output><jupyter_text>Deques and Other Queues  Example 2-23. Working with a deque<jupyter_code>import collections

dq = collections.deque(range(10), maxlen=10)
dq
dq.rotate(3)
dq
dq.rotate(-4)
dq
dq.appendleft(-1)
dq
dq.extend([11, 22, 33])
dq
dq.extendleft([10, 20, 30, 40])
dq<jupyter_output><empty_output><jupyter_text>Soapbox  Mixed bag lists<jupyter_code>l = [28, 14, '28', 5, '9', '1', 0, 6, '23', 19]
try:
    sorted(l)
except TypeError as e:
    print(repr(e))<jupyter_output>TypeError("'<' not supported between instances of 'str' and 'int'")<jupyter_text>Key is Brilliant<jupyter_code>l = [28, 14, '28', 5, '9', '1', 0, 6, '23', 19]

sorted(l, key=int)
sorted(l, key=str)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15338882282996433, 'fraction_numerical': 0.057074910820451845, 'mean_word_length': 4.176856791136643, 'pattern_counts': {'":': 3, '<': 141, '<?xml version=': 0, '>': 142, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '36', 'dir': 'jupyter-structured-clean-dedup', 'id': '376997', 'license': 'MIT', 'n_tokens_mistral': 5441, 'n_tokens_neox': 4919, 'n_words': 1218, 'path': '02-array-seq/array-seq.ipynb', 'repo_name': 'ScottMcCormack/example-code-2e', 'size': '12615'}