<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)* [Slicing](Slicing)* [Building Lists of Lists](Building-Lists-of-Lists)* [Augmented Assignment with Sequences](Augmented-Assignment-with-Sequences)* [list.sort and the sorted Built-In Function](list.sort-and-the-sorted-Built-In-Function)* [Managing Ordered Sequences with bisect](Managing-Ordered-Sequences-with-bisect)* [Arrays](Arrays)* [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, take 2<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<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>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-9. 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-10. 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-11. 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<jupyter_code>t = (1, 2, [30, 40])
try:
    t[2] += [50, 60]
except TypeError as e:
    print(repr(e))
t<jupyter_output><empty_output><jupyter_text>Example 2-14. 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>Managing Ordered Sequences with bisect  Example 2-15. bisect finds insertion points for items in a sorted sequence<jupyter_code># BEGIN BISECT_DEMO
import bisect
import sys

HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]

ROW_FMT = '{0:2d} @ {1:2d}    {2}{0:<2d}'

def demo(haystack, needles, bisect_fn):
    print('DEMO:', bisect_fn.__name__)  # <1>
    print('haystack ->', ' '.join('%2d' % n for n in haystack))
    for needle in reversed(needles):
        position = bisect_fn(haystack, needle)  # <2>
        offset = position * '  |'  # <3>
        print(ROW_FMT.format(needle, position, offset))  # <4>

demo(HAYSTACK, NEEDLES, bisect.bisect)  # <5>
# END BISECT_DEMO
demo(HAYSTACK, NEEDLES, bisect.bisect_left)<jupyter_output>DEMO: bisect_left
haystack ->  1  4  5  6  8 12 15 20 21 23 23 26 29 30
31 @ 14      |  |  |  |  |  |  |  |  |  |  |  |  |  |31
30 @ 13      |  |  |  |  |  |  |  |  |  |  |  |  |30
29 @ 12      |  |  |  |  |  |  |  |  |  |  |  |29
23 @  9      |  |  |  |  |  |  |  |  |23
22 @  9      |  |  |  |  |  |  |  |  |22
10 @  5      |  |  |  |  |10
 8 @  4      |  |  |  |8 
 5 @  2      |  |5 
 2 @  1      |2 
 1 @  0    1 
 0 @  0    0<jupyter_text>Example 2-16. Given a test score, grade returns the corresponding letter grade<jupyter_code>def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    i = bisect.bisect(breakpoints, score)
    return grades[i]

[grade(score) for score in [55, 60, 65, 70, 75, 80, 85, 90, 95]]<jupyter_output><empty_output><jupyter_text>Example 2-17. bisect_left maps a score of 60 to grade F, not D as in Example 2-16.<jupyter_code>def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    i = bisect.bisect_left(breakpoints, score)
    return grades[i]

[grade(score) for score in [55, 60, 65, 70, 75, 80, 85, 90, 95]]<jupyter_output><empty_output><jupyter_text>Example 2-18. Insort keeps a sorted sequence always sorted<jupyter_code>import bisect
import random

SIZE = 7

random.seed(1729)

my_list = []

for i in range(SIZE):
    new_item = random.randrange(SIZE*2)
    bisect.insort(my_list, new_item)
    print(f'insert {new_item:2d} -> {my_list}')<jupyter_output>insert 10 -> [10]
insert  0 -> [0, 10]
insert  6 -> [0, 6, 10]
insert  8 -> [0, 6, 8, 10]
insert  7 -> [0, 6, 7, 8, 10]
insert  2 -> [0, 2, 6, 7, 8, 10]
insert 10 -> [0, 2, 6, 7, 8, 10, 10]<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

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. Changing the value of an 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 and SciPy  Example 2-21. 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-22. 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.14927921092564492, 'fraction_numerical': 0.057947647951441576, 'mean_word_length': 3.7997269003186163, 'pattern_counts': {'":': 0, '<': 127, '<?xml version=': 0, '>': 136, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '31', 'dir': 'jupyter-structured-clean-dedup', 'id': '139586', 'license': 'MIT', 'n_tokens_mistral': 4662, 'n_tokens_neox': 4187, 'n_words': 1069, 'path': '02-array-seq/array-seq.ipynb', 'repo_name': 'p-j-smith/example-code-2e', 'size': '10544'}