Mastering Python Second Edition Release Code

This commit is contained in:
Rick van Hattem
2022-05-05 18:25:55 +02:00
commit 3223a43fe3
454 changed files with 20230 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
Chapter 4, Functional Programming
##############################################################################
| Readability versus Brevity covers the functional programming techniques such as list/dict/set comprehensions and lambda statements that are available in Python. Additionally, it illustrates the similarities to the mathematical principles involved.
@@ -0,0 +1,17 @@
>>> def add_value_functional(items, value):
... return items + [value]
>>> items = [1, 2, 3]
>>> add_value_functional(items, 5)
[1, 2, 3, 5]
>>> items
[1, 2, 3]
>>> def add_value_regular(items, value):
... items.append(value)
... return items
>>> add_value_regular(items, 5)
[1, 2, 3, 5]
>>> items
[1, 2, 3, 5]
@@ -0,0 +1,129 @@
>>> squares = [x ** 2 for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
------------------------------------------------------------------------------
>>> odd_squares = [x ** 2 for x in range(10) if x % 2]
>>> odd_squares
[1, 9, 25, 49, 81]
------------------------------------------------------------------------------
>>> def square(x):
... return x ** 2
>>> def odd(x):
... return x % 2
>>> squares = list(map(square, range(10)))
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> odd_squares = list(filter(odd, map(square, range(10))))
>>> odd_squares
[1, 9, 25, 49, 81]
------------------------------------------------------------------------------
>>> import os
>>> directories = filter(os.path.isdir, os.listdir('.'))
# Versus:
>>> directories = [x for x in os.listdir('.') if os.path.isdir(x)]
------------------------------------------------------------------------------
>>> odd_squares = []
>>> for x in range(10):
... if x % 2:
... odd_squares.append(x ** 2)
>>> odd_squares
[1, 9, 25, 49, 81]
------------------------------------------------------------------------------
# List comprehension
>>> [x // 2 for x in range(3)]
[0, 0, 1]
# Set comprehension
>>> numbers = {x // 2 for x in range(3)}
>>> sorted(numbers)
[0, 1]
------------------------------------------------------------------------------
>>> import random
>>> [random.random() for _ in range(10) if random.random() >= 0.5]
... # doctest: +SKIP
[0.5211948104577864, 0.650010512129705, 0.021427316545174158]
------------------------------------------------------------------------------
>>> import random
>>> numbers = [random.random() for _ in range(10)] # doctest: +SKIP
>>> [x for x in numbers if x >= 0.5] # doctest: +SKIP
[0.715510247827078, 0.8426277505519564, 0.5071133900377911]
------------------------------------------------------------------------------
>>> import random
>>> [x for x in [random.random() for _ in range(10)] if x >= 0.5]
... # doctest: +SKIP
------------------------------------------------------------------------------
>>> import random
>>> [x for _ in range(10) for x in [random.random()] if x >= 0.5]
... # doctest: +SKIP
------------------------------------------------------------------------------
>>> [(x, y) for x in range(3) for y in range(3, 5)]
[(0, 3), (0, 4), (1, 3), (1, 4), (2, 3), (2, 4)]
------------------------------------------------------------------------------
>>> results = []
>>> for x in range(3):
... for y in range(3, 5):
... results.append((x, y))
...
>>> results
[(0, 3), (0, 4), (1, 3), (1, 4), (2, 3), (2, 4)]
------------------------------------------------------------------------------
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
>>> reshaped_matrix = [
... [
... [y for x in matrix for y in x][i * len(matrix) + j]
... for j in range(len(matrix))
... ]
... for i in range(len(matrix[0]))
... ]
>>> import pprint
>>> pprint.pprint(reshaped_matrix, width=40)
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]]
@@ -0,0 +1,10 @@
>>> {x: x ** 2 for x in range(6)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
>>> {x: x ** 2 for x in range(6) if x % 2}
{1: 1, 3: 9, 5: 25}
------------------------------------------------------------------------------
>>> {x ** 2: [y for y in range(x)] for x in range(5)}
{0: [], 1: [0], 4: [0, 1], 9: [0, 1, 2], 16: [0, 1, 2, 3]}
@@ -0,0 +1,5 @@
>>> [x*y for x in range(3) for y in range(3)]
[0, 0, 0, 0, 1, 2, 0, 2, 4]
>>> {x*y for x in range(3) for y in range(3)}
{0, 1, 2, 4}
@@ -0,0 +1,25 @@
>>> import operator
>>> values = dict(one=1, two=2, three=3)
>>> sorted(values.items())
[('one', 1), ('three', 3), ('two', 2)]
>>> sorted(values.items(), key=lambda item: item[1])
[('one', 1), ('two', 2), ('three', 3)]
>>> get_value = operator.itemgetter(1)
>>> sorted(values.items(), key=get_value)
[('one', 1), ('two', 2), ('three', 3)]
------------------------------------------------------------------------------
>>> key = lambda item: item[1]
>>> def key(item):
... return item[1]
------------------------------------------------------------------------------
>>> def key(spam): return spam.value
>>> key = lambda spam: spam.value
@@ -0,0 +1,54 @@
::
Y = lambda f: lambda *args: f(Y(f))(*args)
------------------------------------------------------------------------------
::
def Y(f):
def y(*args):
y_function = f(Y(f))
return y_function(*args)
return y
------------------------------------------------------------------------------
>>> Y = lambda f: lambda *args: f(Y(f))(*args)
>>> def factorial(combinator):
... def _factorial(n):
... if n:
... return n * combinator(n - 1)
... else:
... return 1
... return _factorial
>>> Y(factorial)(5)
120
------------------------------------------------------------------------------
>>> Y = lambda f: lambda *args: f(Y(f))(*args)
>>> Y(lambda c: lambda n: n and n * c(n - 1) or 1)(5)
120
------------------------------------------------------------------------------
>>> Y = lambda f: lambda *args: f(Y(f))(*args)
>>> Y(lambda c: lambda n: n * c(n - 1) if n else 1)(5)
120
------------------------------------------------------------------------------
>>> quicksort = Y(lambda f:
... lambda x: (
... f([item for item in x if item < x[0]])
... + [y for y in x if x[0] == y]
... + f([item for item in x if item > x[0]])
... ) if x else [])
>>> quicksort([1, 3, 5, 4, 1, 3, 2])
[1, 1, 2, 3, 3, 4, 5]
@@ -0,0 +1,50 @@
>>> import heapq
>>> heap = []
>>> heapq.heappush(heap, 1)
>>> heapq.heappush(heap, 3)
>>> heapq.heappush(heap, 5)
>>> heapq.heappush(heap, 2)
>>> heapq.heappush(heap, 4)
>>> heapq.nsmallest(3, heap)
[1, 2, 3]
------------------------------------------------------------------
>>> def push(*args, **kwargs):
... return heapq.heappush(heap, *args, **kwargs)
------------------------------------------------------------------
>>> import functools
>>> import heapq
>>> heap = []
>>> push = functools.partial(heapq.heappush, heap)
>>> smallest = functools.partial(heapq.nsmallest, iterable=heap)
>>> push(1)
>>> push(3)
>>> push(5)
>>> push(2)
>>> push(4)
>>> smallest(3)
[1, 2, 3]
------------------------------------------------------------------
>>> lambda_push = lambda x: heapq.heappush(heap, x)
>>> heapq.heappush
<built-in function heappush>
>>> push
functools.partial(<built-in function heappush>, [1, 2, 5, 3, 4])
>>> lambda_push
<function <lambda> at ...>
>>> heapq.heappush.__doc__
'Push item onto heap, maintaining the heap invariant.'
>>> push.__doc__
'partial(func, *args, **keywords) - new function ...'
>>> lambda_push.__doc__
@@ -0,0 +1,86 @@
>>> import operator
>>> import functools
>>> functools.reduce(operator.mul, range(1, 5))
24
------------------------------------------------------------------------------
>>> from operator import mul
>>> mul(mul(mul(1, 2), 3), 4)
24
------------------------------------------------------------------------------
>>> import operator
>>> def reduce(function, iterable):
... print(f'iterable={iterable}')
... # Fetch the first item to prime `result`
... result, *iterable = iterable
...
... for item in iterable:
... old_result = result
... result = function(result, item)
... print(f'{old_result} * {item} = {result}')
...
... return result
>>> iterable = list(range(1, 5))
>>> iterable
[1, 2, 3, 4]
>>> reduce(operator.mul, iterable)
iterable=[1, 2, 3, 4]
1 * 2 = 2
2 * 3 = 6
6 * 4 = 24
24
------------------------------------------------------------------------------
>>> import operator
>>> iterable = range(1, 5)
# The initial values:
>>> a, b, *iterable = iterable
>>> a, b, iterable
(1, 2, [3, 4])
# First run
>>> a = operator.mul(a, b)
>>> b, *iterable = iterable
>>> a, b, iterable
(2, 3, [4])
# Second run
>>> a = operator.mul(a, b)
>>> b, *iterable = iterable
>>> a, b, iterable
(6, 4, [])
# Third and last run
>>> a = operator.mul (a, b)
>>> a
24
------------------------------------------------------------------------------
>>> import operator
>>> import collections
>>> iterable = collections.deque(range(1, 5))
>>> value = iterable.popleft()
>>> while iterable:
... value = operator.mul(value, iterable.popleft())
>>> value
24
@@ -0,0 +1,73 @@
>>> import json
>>> import functools
>>> import collections
>>> def tree():
... return collections.defaultdict(tree)
# Build the tree:
>>> taxonomy = tree()
>>> reptilia = taxonomy['Chordata']['Vertebrata']['Reptilia']
>>> reptilia['Squamata']['Serpentes']['Pythonidae'] = [
... 'Liasis', 'Morelia', 'Python']
# The actual contents of the tree
>>> print(json.dumps(taxonomy, indent=4))
{
"Chordata": {
"Vertebrata": {
"Reptilia": {
"Squamata": {
"Serpentes": {
"Pythonidae": [
"Liasis",
"Morelia",
"Python"
]
}
}
}
}
}
}
# Let's build the lookup function
>>> import operator
>>> def lookup(tree, path):
... # Split the path for easier access
... path = path.split('.')
...
... # Use `operator.getitem(a, b)` to get `a[b]`
... # And use reduce to recursively fetch the items
... return functools.reduce(operator.getitem, path, tree)
>>> path = 'Chordata.Vertebrata.Reptilia.Squamata.Serpentes'
>>> dict(lookup(taxonomy, path))
{'Pythonidae': ['Liasis', 'Morelia', 'Python']}
# The path we wish to get
>>> path = 'Chordata.Vertebrata.Reptilia.Squamata'
>>> lookup(taxonomy, path).keys()
dict_keys(['Serpentes'])
------------------------------------------------------------------------------
>>> fold_left = lambda iterable, initializer=None: functools.reduce(
... lambda x, y: function(x, y),
... iterable,
... initializer,
... )
>>> fold_right = lambda iterable, initializer=None: functools.reduce(
... lambda x, y: function(y, x),
... reversed(iterable),
... initializer,
... )
@@ -0,0 +1,9 @@
>>> import operator
>>> import itertools
# Sales per month
>>> months = [10, 8, 5, 7, 12, 10, 5, 8, 15, 3, 4, 2]
>>> list(itertools.accumulate(months, operator.add))
[10, 18, 23, 30, 42, 52, 57, 65, 80, 83, 87, 89]
@@ -0,0 +1,13 @@
>>> import itertools
>>> a = range(3)
>>> b = range(5)
>>> list(itertools.chain(a, b))
[0, 1, 2, 0, 1, 2, 3, 4]
>>> import itertools
>>> iterables = [range(3), range(5)]
>>> list(itertools.chain.from_iterable(iterables))
[0, 1, 2, 0, 1, 2, 3, 4]
@@ -0,0 +1,24 @@
>>> import itertools
>>> list(itertools.compress(range(1000), [0, 1, 1, 1, 0, 1]))
[1, 2, 3, 5]
>>> primes = [0, 0, 1, 1, 0, 1, 0, 1]
>>> odd = [0, 1, 0, 1, 0, 1, 0, 1]
>>> numbers = ['zero', 'one', 'two', 'three', 'four', 'five']
# Primes:
>>> list(itertools.compress(numbers, primes))
['two', 'three', 'five']
# Odd numbers
>>> list(itertools.compress(numbers, odd))
['one', 'three', 'five']
# Odd primes
>>> list(itertools.compress(numbers, map(all, zip(odd, primes))))
['three', 'five']
@@ -0,0 +1,9 @@
>>> import itertools
>>> list(itertools.dropwhile(lambda x: x <= 3, [1, 3, 5, 4, 2]))
[5, 4, 2]
>>> import itertools
>>> list(itertools.takewhile(lambda x: x <= 3, [1, 3, 5, 4, 2]))
[1, 3]
@@ -0,0 +1,11 @@
>>> import itertools
>>> list(itertools.islice(itertools.count(), 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(itertools.islice(itertools.count(), 5, 10, 2))
[5, 7, 9]
>>> list(itertools.islice(itertools.count(10, 2.5), 5))
[10, 12.5, 15.0, 17.5, 20.0]
@@ -0,0 +1,34 @@
>>> import operator
>>> import itertools
>>> words = ['aa', 'ab', 'ba', 'bb', 'ca', 'cb', 'cc']
# Gets the first element from the iterable
>>> getter = operator.itemgetter(0)
>>> for group, items in itertools.groupby(words, key=getter):
... print(f'group: {group}, items: {list(items)}')
group: a, items: ['aa', 'ab']
group: b, items: ['ba', 'bb']
group: c, items: ['ca', 'cb', 'cc']
------------------------------------------------------------
>>> import operator
>>> import itertools
>>> words = ['aa', 'bb', 'ca', 'ab', 'ba', 'cb', 'cc']
# Gets the first element from the iterable
>>> getter = operator.itemgetter(0)
>>> for group, items in itertools.groupby(words, key=getter):
... print(f'group: {group}, items: {list(items)}')
group: a, items: ['aa']
group: b, items: ['bb']
group: c, items: ['ca']
group: a, items: ['ab']
group: b, items: ['ba']
group: c, items: ['cb', 'cc']