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 3, Containers and Collections
------------------------------------------------------------------------------
| Storing Data the Right Way using the many containers and collections bundled with Python to create code that is fast and readable.
@@ -0,0 +1,34 @@
>>> n = 1000
>>> a = list(range(n))
>>> b = dict.fromkeys(range(n))
>>> for i in range(100):
... assert i in a # takes n=1000 steps
... assert i in b # takes 1 step
>>> def o_one(items):
... return 1 # 1 operation so O(1)
>>> def o_n(items):
... total = 0
... # Walks through all items once so O(n)
... for item in items:
... total += item
... return total
>>> def o_n_squared(items):
... total = 0
... # Walks through all items n*n times so O(n**2)
... for a in items:
... for b in items:
... total += a * b
... return total
>>> n = 10
>>> items = range(n)
>>> o_one(items) # 1 operation
1
>>> o_n(items) # n = 10 operations
45
>>> o_n_squared(items) # n*n = 10*10 = 100 operations
2025
+89
View File
@@ -0,0 +1,89 @@
>>> def remove(items, value):
... new_items = []
... found = False
... for item in items:
... # Skip the first item which is equal to value
... if not found and item == value:
... found = True
... continue
... new_items.append(item)
...
... if not found:
... raise ValueError('list.remove(x): x not in list')
...
... return new_items
>>> def insert(items, index, value):
... new_items = []
... for i, item in enumerate(items):
... if i == index:
... new_items.append(value)
... new_items.append(item)
... return new_items
>>> items = list(range(10))
>>> items
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> items = remove(items, 5)
>>> items
[0, 1, 2, 3, 4, 6, 7, 8, 9]
>>> items = insert(items, 2, 5)
>>> items
[0, 1, 5, 2, 3, 4, 6, 7, 8, 9]
------------------------------------------------------------------------------
>>> primes = set((1, 2, 3, 5, 7))
# Classic solution
>>> items = list(range(10))
>>> for prime in primes:
... items.remove(prime)
>>> items
[0, 4, 6, 8, 9]
# List comprehension
>>> items = list(range(10))
>>> [item for item in items if item not in primes]
[0, 4, 6, 8, 9]
# Filter
>>> items = list(range(10))
>>> list(filter(lambda item: item not in primes, items))
[0, 4, 6, 8, 9]
------------------------------------------------------------------------------
>>> def in_(items, value):
... for item in items:
... if item == value:
... return True
... return False
>>> def min_(items):
... current_min = items[0]
... for item in items[1:]:
... if current_min > item:
... current_min = item
... return current_min
>>> def max_(items):
... current_max = items[0]
... for item in items[1:]:
... if current_max < item:
... current_max = item
... return current_max
>>> items = range(5)
>>> in_(items, 3)
True
>>> min_(items)
0
>>> max_(items)
4
+48
View File
@@ -0,0 +1,48 @@
>>> def most_significant(value):
... while value >= 10:
... value //= 10
... return value
>>> most_significant(12345)
1
>>> most_significant(99)
9
>>> most_significant(0)
0
------------------------------------------------------------------------------
>>> def add(collection, key, value):
... index = most_significant(key)
... collection[index].append((key, value))
>>> def contains(collection, key):
... index = most_significant(key)
... for k, v in collection[index]:
... if k == key:
... return True
... return False
# Create the collection of 10 lists
>>> collection = [[], [], [], [], [], [], [], [], [], []]
# Add some items, using key/value pairs
>>> add(collection, 123, 'a')
>>> add(collection, 456, 'b')
>>> add(collection, 789, 'c')
>>> add(collection, 101, 'c')
# Look at the collection
>>> collection
[[], [(123, 'a'), (101, 'c')], [], [],
[(456, 'b')], [], [], [(789, 'c')], [], []]
# Check if the contains works correctly
>>> contains(collection, 123)
True
>>> contains(collection, 1)
False
+38
View File
@@ -0,0 +1,38 @@
# All output in the table below is generated using this function
>>> def print_set(expression, set_):
... 'Print set as a string sorted by letters'
... print(expression, ''.join(sorted(set_)))
>>> spam = set('spam')
>>> print_set('spam:', spam)
spam: amps
>>> eggs = set('eggs')
>>> print_set('eggs:', eggs)
eggs: egs
------------------------------------------------------------------------------
>>> current_users = set((
... 'a',
... 'b',
... 'd',
... ))
>>> new_users = set((
... 'b',
... 'c',
... 'd',
... 'e',
... ))
>>> to_insert = new_users - current_users
>>> sorted(to_insert)
['c', 'e']
>>> to_delete = current_users - new_users
>>> sorted(to_delete)
['a']
>>> unchanged = new_users & current_users
>>> sorted(unchanged)
['b', 'd']
+99
View File
@@ -0,0 +1,99 @@
>>> spam = 1, 2, 3
>>> eggs = 4, 5, 6
>>> data = dict()
>>> data[spam] = 'spam'
>>> data[eggs] = 'eggs'
>>> import pprint # Using pprint for consistent and sorted output
>>> pprint.pprint(data)
{(1, 2, 3): 'spam', (4, 5, 6): 'eggs'}
------------------------------------------------------------------------------
>>> spam = 1, 'abc', (2, 3, (4, 5)), 'def'
>>> eggs = 4, (spam, 5), 6
>>> data = dict()
>>> data[spam] = 'spam'
>>> data[eggs] = 'eggs'
>>> import pprint # Using pprint for consistent and sorted output
>>> pprint.pprint(data)
{(1, 'abc', (2, 3, (4, 5)), 'def'): 'spam',
(4, ((1, 'abc', (2, 3, (4, 5)), 'def'), 5), 6): 'eggs'}
------------------------------------------------------------------------------
# Assign using tuples on both sides
>>> a, b, c = 1, 2, 3
>>> a
1
# Assign a tuple to a single variable
>>> spam = a, (b, c)
>>> spam
(1, (2, 3))
# Unpack a tuple to two variables
>>> a, b = spam
>>> a
1
>>> b
(2, 3)
------------------------------------------------------------------------------
# Unpack with variable length objects which assigns a list instead
of a tuple
>>> spam, *eggs = 1, 2, 3, 4
>>> spam
1
>>> eggs
[2, 3, 4]
# Which can be unpacked as well of course
>>> a, b, c = eggs
>>> c
4
# This works for ranges as well
>>> spam, *eggs = range(10)
>>> spam
0
>>> eggs
[1, 2, 3, 4, 5, 6, 7, 8, 9]
# And it works both ways
>>> a, b, *c = a, *eggs
>>> a, b
(2, 1)
>>> c
[2, 3, 4, 5, 6, 7, 8, 9]
------------------------------------------------------------------------------
>>> def eggs(*args):
... print('args:', args)
>>> eggs(1, 2, 3)
args: (1, 2, 3)
------------------------------------------------------------------------------
>>> def spam_eggs():
... return 'spam', 'eggs'
>>> spam, eggs = spam_eggs()
>>> spam
'spam'
>>> eggs
'eggs'
@@ -0,0 +1,59 @@
>>> spam: int
>>> __annotations__['spam']
<class 'int'>
>>> spam = 'not a number'
>>> __annotations__['spam']
<class 'int'>
>>> import dataclasses
>>> @dataclasses.dataclass
... class Sandwich:
... spam: int
... eggs: int = 3
>>> Sandwich(1, 2)
Sandwich(spam=1, eggs=2)
>>> sandwich = Sandwich(4)
>>> sandwich
Sandwich(spam=4, eggs=3)
>>> sandwich.eggs
3
>>> dataclasses.asdict(sandwich)
{'spam': 4, 'eggs': 3}
>>> dataclasses.astuple(sandwich)
(4, 3)
>>> help(dataclasses.dataclass)
Help on ... dataclass(..., *, init=True, repr=True, eq=True, ...
>>> def __init__(self, spam, eggs=3):
... self.spam = spam
... self.eggs = eggs
>>> import typing
>>> @dataclasses.dataclass
... class Group:
... name: str
... parent: 'Group' = None
>>> @dataclasses.dataclass
... class User:
... username: str
... email: str = None
... groups: typing.List[Group] = None
>>> users = Group('users')
>>> admins = Group('admins', users)
>>> rick = User('rick', groups=[admins])
>>> gvr = User('gvanrossum', 'guido@python.org', [admins])
>>> rick.groups
[Group(name='admins', parent=Group(name='users', parent=None))]
>>> rick.groups[0].parent
Group(name='users', parent=None)
+74
View File
@@ -0,0 +1,74 @@
>>> import builtins
>>> builtin_vars = vars(builtins)
>>> key = 'something to search for'
>>> if key in locals():
... value = locals()[key]
... elif key in globals():
... value = globals()[key]
... elif key in builtin_vars:
... value = builtin_vars[key]
... else:
... raise NameError(f'name {key!r} is not defined')
Traceback (most recent call last):
...
NameError: name 'something to search for' is not defined
##############################################################################
>>> mappings = locals(), globals(), vars(builtins)
>>> for mapping in mappings:
... if key in mapping:
... value = mapping[key]
... break
... else:
... raise NameError(f'name {key!r} is not defined')
Traceback (most recent call last):
...
NameError: name 'something to search for' is not defined
##############################################################################
>>> import collections
>>> mappings = collections.ChainMap(
... locals(), globals(), vars(builtins))
>>> mappings[key]
Traceback (most recent call last):
...
KeyError: 'something to search for'
##############################################################################
>>> import json
>>> import pathlib
>>> import argparse
>>> import collections
>>> DEFAULT = dict(verbosity=1)
>>> config_file = pathlib.Path('config.json')
>>> if config_file.exists():
... config = json.load(config_file.open())
... else:
... config = dict()
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-v', '--verbose', action='count',
... dest='verbosity')
_CountAction(...)
>>> args, _ = parser.parse_known_args(args=['-v'])
>>> defined_args = {k: v for k, v in vars(args).items() if v}
>>> combined = collections.ChainMap(defined_args, config, DEFAULT)
>>> combined['verbosity']
1
>>> args, _ = parser.parse_known_args(['-vv'])
>>> defined_args = {k: v for k, v in vars(args).items() if v}
>>> combined = collections.ChainMap(defined_args, config, DEFAULT)
>>> combined['verbosity']
2
@@ -0,0 +1,93 @@
>>> nodes = [
... ('a', 'b'),
... ('a', 'c'),
... ('b', 'a'),
... ('b', 'd'),
... ('c', 'a'),
... ('d', 'a'),
... ('d', 'b'),
... ('d', 'c'),
... ]
------------------------------------------------------------------------------
>>> graph = dict()
>>> for from_, to in nodes:
... if from_ not in graph:
... graph[from_] = []
... graph[from_].append(to)
>>> import pprint
>>> pprint.pprint(graph)
{'a': ['b', 'c'],
'b': ['a', 'd'],
'c': ['a'],
'd': ['a', 'b', 'c']}
------------------------------------------------------------------------------
>>> import collections
>>> graph = collections.defaultdict(list)
>>> for from_, to in nodes:
... graph[from_].append(to)
>>> import pprint
>>> pprint.pprint(graph)
defaultdict(<class 'list'>,
{'a': ['b', 'c'],
'b': ['a', 'd'],
'c': ['a'],
'd': ['a', 'b', 'c']})
------------------------------------------------------------------------------
>>> counter = collections.defaultdict(int)
>>> counter['spam'] += 5
>>> counter
defaultdict(<class 'int'>, {'spam': 5})
------------------------------------------------------------------------------
>>> import collections
>>> def tree(): return collections.defaultdict(tree)
------------------------------------------------------------------------------
>>> import json
>>> import collections
>>> def tree():
... return collections.defaultdict(tree)
>>> colours = tree()
>>> colours['other']['black'] = 0x000000
>>> colours['other']['white'] = 0xFFFFFF
>>> colours['primary']['red'] = 0xFF0000
>>> colours['primary']['green'] = 0x00FF00
>>> colours['primary']['blue'] = 0x0000FF
>>> colours['secondary']['yellow'] = 0xFFFF00
>>> colours['secondary']['aqua'] = 0x00FFFF
>>> colours['secondary']['fuchsia'] = 0xFF00FF
>>> print(json.dumps(colours, sort_keys=True, indent=4))
{
"other": {
"black": 0,
"white": 16777215
},
"primary": {
"blue": 255,
"green": 65280,
"red": 16711680
},
"secondary": {
"aqua": 65535,
"fuchsia": 16711935,
"yellow": 16776960
}
}
+60
View File
@@ -0,0 +1,60 @@
>>> import enum
>>> class Color(enum.Enum):
... red = 1
... green = 2
... blue = 3
>>> Color.red
<Color.red: 1>
>>> Color['red']
<Color.red: 1>
>>> Color(1)
<Color.red: 1>
>>> Color.red.name
'red'
>>> Color.red.value
1
>>> isinstance(Color.red, Color)
True
>>> Color.red is Color['red']
True
>>> Color.red is Color(1)
True
------------------------------------------------------------------------------
>>> for color in Color:
... color
<Color.red: 1>
<Color.green: 2>
<Color.blue: 3>
>>> colors = dict()
>>> colors[Color.green] = 0x00FF00
>>> colors
{<Color.green: 2>: 65280}
------------------------------------------------------------------------------
>>> import enum
>>> class Spam(enum.Enum):
... EGGS = 'eggs'
>>> Spam.EGGS == 'eggs'
False
------------------------------------------------------------------------------
>>> import enum
>>> class Spam(str, enum.Enum):
... EGGS = 'eggs'
>>> Spam.EGGS == 'eggs'
True
+29
View File
@@ -0,0 +1,29 @@
>>> import heapq
>>> heap = [1, 3, 5, 7, 2, 4, 3]
>>> heapq.heapify(heap)
>>> heap
[1, 2, 3, 7, 3, 4, 5]
>>> while heap:
... heapq.heappop(heap), heap
(1, [2, 3, 3, 7, 5, 4])
(2, [3, 3, 4, 7, 5])
(3, [3, 5, 4, 7])
(3, [4, 5, 7])
(4, [5, 7])
(5, [7])
(7, [])
>>> def heapsort(iterable):
... heap = []
... for value in iterable:
... heapq.heappush(heap, value)
...
... while heap:
... yield heapq.heappop(heap)
>>> list(heapsort([1, 3, 5, 2, 4, 1]))
[1, 1, 2, 3, 4, 5]
+99
View File
@@ -0,0 +1,99 @@
>>> import bisect
Using the regular sort:
>>> sorted_list = []
>>> sorted_list.append(5) # O(1)
>>> sorted_list.append(3) # O(1)
>>> sorted_list.append(1) # O(1)
>>> sorted_list.append(2) # O(1)
>>> sorted_list.sort() # O(n * log(n)) = 4 * log(4) = 8
>>> sorted_list
[1, 2, 3, 5]
Using bisect:
>>> sorted_list = []
>>> bisect.insort(sorted_list, 5) # O(n) = 1
>>> bisect.insort(sorted_list, 3) # O(n) = 2
>>> bisect.insort(sorted_list, 1) # O(n) = 3
>>> bisect.insort(sorted_list, 2) # O(n) = 4
>>> sorted_list
[1, 2, 3, 5]
------------------------------------------------------------------------------
>>> sorted_list = [1, 2, 5]
>>> def contains(sorted_list, value):
... for item in sorted_list:
... if item > value:
... break
... elif item == value:
... return True
... return False
>>> contains(sorted_list, 2) # Need to walk through 2 items, O(n) = 2
True
>>> contains(sorted_list, 4) # Need to walk through n items, O(n) = 3
False
>>> contains(sorted_list, 6) # Need to walk through n items, O(n) = 3
False
>>> import bisect
>>> sorted_list = [1, 2, 5]
>>> def contains(sorted_list, value):
... i = bisect.bisect_left(sorted_list, value)
... return i < len(sorted_list) and sorted_list[i] == value
>>> contains(sorted_list, 2) # Found it the first step, O(log(n)) = 1
True
>>> contains(sorted_list, 4) # No result after 2 steps, O(log(n)) = 2
False
>>> contains(sorted_list, 6) # No result after 2 steps, O(log(n)) = 2
False
>>> import bisect
>>> import collections
>>> class SortedList:
... def __init__(self, *values):
... self._list = sorted(values)
...
... def index(self, value):
... i = bisect.bisect_left(self._list, value)
... if i < len(self._list) and self._list[i] == value:
... return index
...
... def delete(self, value):
... del self._list[self.index(value)]
...
... def add(self, value):
... bisect.insort(self._list, value)
...
... def __iter__(self):
... for value in self._list:
... yield value
...
... def __exists__(self, value):
... return self.index(value) is not None
>>> sorted_list = SortedList(1, 3, 6, 2)
>>> 3 in sorted_list
True
>>> 5 in sorted_list
False
>>> sorted_list.add(5)
>>> 5 in sorted_list
True
>>> list(sorted_list)
[1, 2, 3, 5, 6]
@@ -0,0 +1,37 @@
>>> class Borg:
... _state = {}
... def __init__(self):
... self.__dict__ = self._state
>>> class SubBorg(Borg):
... pass
>>> a = Borg()
>>> b = Borg()
>>> c = Borg()
>>> a.a_property = 123
>>> b.a_property
123
>>> c.a_property
123
>>> class Singleton:
... def __new__(cls):
... if not hasattr(cls, '_instance'):
... cls._instance = super(Singleton, cls).__new__(cls)
...
... return cls._instance
>>> class SubSingleton(Singleton):
... pass
>>> a = Singleton()
>>> b = Singleton()
>>> c = SubSingleton()
>>> a.a_property = 123
>>> b.a_property
123
>>> c.a_property
123
@@ -0,0 +1,23 @@
>>> class Sandwich:
...
... def __init__(self, spam):
... self.spam = spam
...
... @property
... def spam(self):
... return self._spam
...
... @spam.setter
... def spam(self, value):
... self._spam = value
... if self._spam >= 5:
... print('You must be hungry')
...
... @spam.deleter
... def spam(self):
... self._spam = 0
>>> sandwich = Sandwich(2)
>>> sandwich.spam += 1
>>> sandwich.spam += 2
You must be hungry
+22
View File
@@ -0,0 +1,22 @@
>>> a = dict(x=1, y=2)
>>> b = dict(y=1, z=2)
>>> c = a.copy()
>>> c
{'x': 1, 'y': 2}
>>> c.update(b)
>>> a
{'x': 1, 'y': 2}
>>> b
{'y': 1, 'z': 2}
>>> c
{'x': 1, 'y': 1, 'z': 2}
------------------------------------------------------------------------------
>>> a = dict(x=1, y=2)
>>> b = dict(y=1, z=2)
>>> a | b
{'x': 1, 'y': 1, 'z': 2}
View File