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 2, Pythonic Syntax
##############################################################################
| Common Pitfalls and Style Guide explains what Pythonic code is and how to write code that is Pythonic and adheres to the Python philosophy.
@@ -0,0 +1,78 @@
# Simple formatting
>>> name = 'Rick'
>>> 'Hi %s' % name
'Hi Rick'
>>> 'Hi {}'.format(name)
'Hi Rick'
>>> value = 1 / 3
>>> '%.2f' % value
'0.33'
>>> '{:.2f}'.format(value)
'0.33'
>>> name = 'Rick'
>>> value = 1 / 3
>>> 'Hi {0}, value: {1:.3f}. Bye {0}'.format(name, value)
'Hi Rick, value: 0.333. Bye Rick'
# Named variables
>>> name = 'Rick'
>>> 'Hi %(name)s' % dict(name=name)
'Hi Rick'
>>> 'Hi {name}'.format(name=name)
'Hi Rick'
>>> f'Hi {name}'
'Hi Rick'
>>> 'Hi {name}'.format(**globals())
'Hi Rick'
# Arbitrary expressions
## Accessing dict items
>>> username = 'wolph'
>>> a = 123
>>> b = 456
>>> some_dict = dict(a=a, b=b)
>>> f'''a: {some_dict['a']}'''
'a: 123'
>>> f'''sum: {some_dict['a'] + some_dict['b']}'''
'sum: 579'
## Python expressions, specifically an inline if statement
>>> f'if statement: {a if a > b else b}'
'if statement: 456'
## Function calls
>>> f'min: {min(a, b)}'
'min: 123'
>>> f'Hi {username}. And in uppercase: {username.upper()}'
'Hi wolph. And in uppercase: WOLPH'
## Loops
>>> f'Squares: {[x ** 2 for x in range(5)]}'
'Squares: [0, 1, 4, 9, 16]'
+22
View File
@@ -0,0 +1,22 @@
>>> import this
The Zen of Python, by Tim Peters
<BLANKLINE>
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
@@ -0,0 +1,14 @@
>>> filter_modulo = lambda i, m: (i[j] for j in \
... range(len(i)) if i[j] % m)
>>> list(filter_modulo(range(10), 2))
[1, 3, 5, 7, 9]
>>> def filter_modulo(items, modulo):
... for item in items:
... if item % modulo:
... yield item
...
>>> list(filter_modulo(range(10), 2))
[1, 3, 5, 7, 9]
@@ -0,0 +1,46 @@
>>> from os import *
>>> from asyncio import *
>>> assert wait
>>> from os import path
>>> from asyncio import wait
>>> assert wait
>>> import os
>>> import asyncio
>>> assert asyncio.wait
>>> assert os.path
>>> import concurrent.futures
>>> assert concurrent.futures.wait
>>> def spam(eggs, *args, **kwargs):
... for arg in args:
... eggs += arg
... for extra_egg in kwargs.get('extra_eggs', []):
... eggs += extra_egg
... return eggs
>>> spam(1, 2, 3, extra_eggs=[4, 5])
15
>>> def sum_ints(*args):
... total = 0
... for arg in args:
... total += arg
... return total
>>> sum_ints(1, 2, 3, 4, 5)
15
@@ -0,0 +1,47 @@
>>> import math
>>> import itertools
>>> def primes_complicated():
... sieved = dict()
... i = 2
...
... while True:
... if i not in sieved:
... yield i
... sieved[i * i] = [i]
... else:
... for j in sieved[i]:
... sieved.setdefault(i + j, []).append(j)
... del sieved[i]
...
... i += 1
>>> list(itertools.islice(primes_complicated(), 10))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
>>> def primes_complex():
... numbers = itertools.count(2)
... while True:
... yield (prime := next(numbers))
... numbers = filter(prime.__rmod__, numbers)
>>> list(itertools.islice(primes_complex(), 10))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
>>> def is_prime(number):
... if number == 0 or number == 1:
... return False
... for modulo in range(2, number):
... if not number % modulo:
... return False
... else:
... return True
>>> def primes_simple():
... for i in itertools.count():
... if is_prime(i):
... yield i
>>> list(itertools.islice(primes_simple(), 10))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
@@ -0,0 +1,27 @@
>>> def between_and_modulo(value, a, b, modulo):
... if value >= a:
... if value <= b:
... if value % modulo:
... return True
... return False
>>> for i in range(10):
... if between_and_modulo(i, 2, 9, 2):
... print(i, end=' ')
3 5 7 9
>>> def between_and_modulo(value, a, b, modulo):
... if value < a:
... return False
... elif value > b:
... return False
... elif not value % modulo:
... return False
... else:
... return True
>>> for i in range(10):
... if between_and_modulo(i, 2, 9, 2):
... print(i, end=' ')
3 5 7 9
@@ -0,0 +1,11 @@
>>> f=lambda x:0**x or x*f(x-1)
>>> f(40)
815915283247897734345611269596115894272000000000
>>> def factorial(x):
... if 0 ** x:
... return 1
... else:
... return x * factorial(x - 1)
>>> factorial(40)
815915283247897734345611269596115894272000000000
@@ -0,0 +1,32 @@
>>> from functools import reduce
>>> fib=lambda n:n if n<2 else fib(n-1)+fib(n-2)
>>> fib(10)
55
>>> fib=lambda n:reduce(lambda x,y:(x[0]+x[1],x[0]),[(1,1)]*(n-1))[0]
>>> fib(10)
55
>>> def fib(n):
... if n < 2:
... return n
... else:
... return fib(n - 1) + fib(n - 2)
>>> fib(10)
55
>>> def fib(n):
... a = 0
... b = 1
... for _ in range(n):
... a, b = b, a + b
...
... return a
>>> fib(10)
55
@@ -0,0 +1,18 @@
>>> from concurrent.futures import ProcessPoolExecutor, \
... CancelledError, TimeoutError
>>> from concurrent.futures import (
... ProcessPoolExecutor, CancelledError, TimeoutError)
>>> from concurrent import futures
>>> from concurrent.futures.process import (
... ProcessPoolExecutor
... )
>>> from concurrent.futures import (
... ProcessPoolExecutor,
... CancelledError,
... TimeoutError,
... )
@@ -0,0 +1,51 @@
>>> some_user_input = '123abc'
>>> try:
... value = int(some_user_input)
... except:
... pass
>>> some_user_input = '123abc'
>>> try:
... value = int(some_user_input)
... except ValueError:
... pass
>>> import logging
>>> some_user_input = '123abc'
>>> try:
... value = int(some_user_input)
... except Exception as exception:
... logging.exception('Uncaught: {exception!r}')
>>> some_user_input_a = '123'
>>> some_user_input_b = 'abc'
>>> try:
... value = int(some_user_input_a)
... value += int(some_user_input_b)
... except:
... value = 0
>>> try:
... 1 / 0 # Raises ZeroDivisionError
... except ZeroDivisionError:
... print('Got zero division error')
... except Exception as exception:
... print(f'Got unexpected exception: {exception}')
... except BaseException as exception:
... # Base exceptions are a special case for keyboard
... # interrupts and a few other exceptions that are not
... # technically errors.
... print(f'Got base exception: {exception}')
... else:
... print('No exceptions happened, we can continue')
... finally:
... # Useful cleanup functions such as closing a file
... print('This code is _always_ executed')
Got zero division error
This code is _always_ executed
+7
View File
@@ -0,0 +1,7 @@
>>> fh_a = open('spam', 'w', -1, None, None, '\n')
>>> fh_b = open(file='spam', mode='w', buffering=-1, newline='\n')
>>> filename = 'spam'
>>> mode = 'w'
>>> buffers = -1
>>> fh_b = open(filename, mode, buffers, newline='\n')
@@ -0,0 +1,3 @@
>>> import warnings
>>> warnings.warn('Something deprecated', DeprecationWarning)
@@ -0,0 +1,9 @@
>>> from json import loads
>>> loads('{}')
{}
>>> import json
>>> json.loads('{}')
{}
@@ -0,0 +1,28 @@
>>> timestamp = 12345
>>> filename = f'{timestamp}.csv'
>>> import datetime
>>> timestamp = 12345
>>> if isinstance(timestamp, datetime.datetime):
... filename = f'{timestamp}.csv'
... else:
... raise TypeError(f'{timestamp} is not a valid datetime')
Traceback (most recent call last):
...
TypeError: 12345 is not a valid datetime
>>> import datetime
>>> timestamp = datetime.date(2000, 10, 5)
>>> filename = f'{timestamp}.csv'
>>> print(f'Filename from date: {filename}')
Filename from date: 2000-10-05.csv
>>> timestamp = '2000-10-05'
>>> filename = f'{timestamp}.csv'
>>> print(f'Filename from str: {filename}')
Filename from str: 2000-10-05.csv
@@ -0,0 +1,58 @@
>>> a = 1
>>> a == True
True
>>> a is True
False
>>> b = 0
>>> b == False
True
>>> b is False
False
>>> def some_unsafe_function(arg=None):
... if not arg:
... arg = 123
...
... return arg
>>> some_unsafe_function(0)
123
>>> some_unsafe_function(None)
123
>>> def some_safe_function(arg=None):
... if arg is None:
... arg = 123
...
... return arg
>>> some_safe_function(0)
0
>>> some_safe_function(None)
123
>>> a = 200 + 56
>>> b = 256
>>> c = 200 + 57
>>> d = 257
>>> a == b
True
>>> a is b
True
>>> c == d
True
>>> c is d
False
>>> spam = list(range(1000000))
>>> eggs = list(range(1000000))
>>> spam == eggs
True
>>> spam is eggs
False
+21
View File
@@ -0,0 +1,21 @@
>>> my_range = range(5)
>>> i = 0
>>> while i < len(my_range):
... item = my_range[i]
... print(i, item, end=', ')
... i += 1
0 0, 1 1, 2 2, 3 3, 4 4,
>>> my_range = range(5)
>>> for item in my_range:
... print(item, end=', ')
0, 1, 2, 3, 4,
>>> for i, item in enumerate(my_range):
... print(i, item, end=', ')
0 0, 1 1, 2 2, 3 3, 4 4,
>>> my_range = range(5)
>>> [(i, item) for i, item in enumerate(my_range)]
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
+18
View File
@@ -0,0 +1,18 @@
def noop():
pass
def yield_cube_points(matrix):
for x in matrix:
for y in x:
for z in y:
yield (x, y, z)
def print_cube(matrix):
for x in matrix:
for y in x:
for z in y:
print(z, end='')
print()
print()
+2
View File
@@ -0,0 +1,2 @@
some_number: int
some_number = 'test'
+2
View File
@@ -0,0 +1,2 @@
def spam(a,b,c):print(a,b+c)
def eggs():pass
@@ -0,0 +1,182 @@
>>> some_variable = 123
>>> match some_variable:
... case 1:
... print('Got 1')
... case 2:
... print('Got 2')
... case _:
... print('Got something else')
Got something else
>>> if some_variable == 1:
... print('Got 1')
... elif some_variable == 1:
... print('Got 2')
... else:
... print('Got something else')
Got something else
##################################################################
>>> some_variable = 123
>>> match some_variable:
... case 1:
... print('Got 1')
... case other:
... print('Got something else:', other)
Got something else: 123
##################################################################
>>> class Direction:
... LEFT = -1
... RIGHT = 1
>>> some_variable = Direction.LEFT
>>> match some_variable:
... case Direction.LEFT:
... print('Going left')
... case Direction.RIGHT:
... print('Going right')
Going left
##################################################################
>>> class Direction:
... LEFT = -1
... UP = 0
... RIGHT = 1
... DOWN = 2
>>> some_variable = Direction.LEFT
>>> match some_variable:
... case Direction.LEFT | Direction.RIGHT:
... print('Going horizontal')
... case Direction.UP | Direction.DOWN:
... print('Going vertical')
Going horizontal
##################################################################
>>> values = -1, 0, 1
>>> for value in values:
... print('matching', value, end=': ')
... match value:
... case negative if negative < 0:
... print(f'{negative} is smaller than 0')
... case positive if positive > 0:
... print(f'{positive} is greater than 0')
... case _:
... print('no match')
matching -1: -1 is smaller than 0
matching 0: no match
matching 1: 1 is greater than 0
##################################################################
>>> values = (0, 1), (0, 2), (1, 2)
>>> for value in values:
... print('matching', value, end=': ')
... match value:
... case 0, 1:
... print('exactly matched 0, 1')
... case 0, y:
... print(f'matched 0, y with y: {y}')
... case x, y:
... print(f'matched x, y with x, y: {x}, {y}')
matching (0, 1): exactly matched 0, 1
matching (0, 2): matched 0, y with y: 2
matching (1, 2): matched x, y with x, y: 1, 2
##################################################################
>>> def get_uri(*args):
... # Set defaults so we only have to store changed variables
... protocol, port, paths = 'https', 443, ()
... match args:
... case (hostname,):
... pass
... case (hostname, port):
... pass
... case (hostname, port, protocol, *paths):
... pass
... case _:
... raise RuntimeError(f'Invalid arguments {args}')
...
... path = '/'.join(paths)
... return f'{protocol}://{hostname}:{port}/{path}'
>>> get_uri('localhost')
'https://localhost:443/'
>>> get_uri('localhost', 12345)
'https://localhost:12345/'
>>> get_uri('localhost', 80, 'http')
'http://localhost:80/'
>>> get_uri('localhost', 80, 'http', 'some', 'paths')
'http://localhost:80/some/paths'
##################################################################
>>> values = (0, 1), (0, 2), (1, 2)
>>> for value in values:
... print('matching', value, end=': ')
... match value:
... case 0 as x, (1 | 2) as y:
... print(f'matched x, y with x, y: {x}, {y}')
... case _:
... print('no match')
matching (0, 1): matched x, y with x, y: 0, 1
matching (0, 2): matched x, y with x, y: 0, 2
matching (1, 2): no match
##################################################################
>>> values = dict(a=0, b=0), dict(a=0, b=1), dict(a=1, b=1)
>>> for value in values:
... print('matching', value, end=': ')
... match value:
... case {'a': 0}:
... print('matched a=0:', value)
... case {'a': 0, 'b': 0}:
... print('matched a=0, b=0:', value)
... case _:
... print('no match')
matching {'a': 0, 'b': 0}: matched a=0: {'a': 0, 'b': 0}
matching {'a': 0, 'b': 1}: matched a=0: {'a': 0, 'b': 1}
matching {'a': 1, 'b': 1}: no match
##################################################################
>>> class Person:
... def __init__(self, name):
... self.name = name
>>> values = Person('Rick'), Person('Guido')
>>> for value in values:
... match value:
... case Person(name='Rick'):
... print('I found Rick')
... case Person(occupation='Programmer'):
... print('I found a programmer')
... case Person() as person:
... print('I found a person:', person.name)
I found Rick
I found a person: Guido
##################################################################
>>> class Person:
... def __init__(self, name):
... self.name = name
>>> value = Person(123)
>>> match value:
... case Person(name=str() as name):
... print('Found person with str name:', name)
... case Person(name=int() as name):
... print('Found person with int name:', name)
Found person with int name: 123
@@ -0,0 +1,18 @@
>>> g = 1
>>> def print_global():
... print(f'Value: {g}')
>>> print_global()
Value: 1
>>> g = 1
>>> def print_global():
... g += 1
... print(f'Value: {g}')
>>> print_global()
Traceback (most recent call last):
...
UnboundLocalError: local variable 'g' referenced before assignment
@@ -0,0 +1,37 @@
>>> import copy
>>> x = [[1], [2, 3]]
>>> y = x.copy()
>>> z = copy.deepcopy(x)
>>> x.append('a')
>>> x[0].append(x)
>>> x
[[1, [...]], [2, 3], 'a']
>>> y
[[1, [...]], [2, 3]]
>>> z
[[1], [2, 3]]
>>> def append(list_=[], value='value'):
... list_.append(value)
... return list_
>>> append(value='a')
['a']
>>> append(value='b')
['a', 'b']
>>> def append(list_=None, value='value'):
... if list_ is None:
... list_ = []
... list_.append(value)
... return list_
>>> append(value='a')
['a']
>>> append(value='b')
['b']
@@ -0,0 +1,47 @@
>>> class SomeClass:
... class_list = []
...
... def __init__(self):
... self.instance_list = []
>>> SomeClass.class_list.append('from class')
>>> instance = SomeClass()
>>> instance.class_list.append('from instance')
>>> instance.instance_list.append('from instance')
>>> SomeClass.class_list
['from class', 'from instance']
>>> SomeClass.instance_list
Traceback (most recent call last):
...
AttributeError: ... 'SomeClass' has no attribute 'instance_list'
>>> instance.class_list
['from class', 'from instance']
>>> instance.instance_list
['from instance']
>>> class Parent:
... pass
>>> class Child(Parent):
... pass
>>> Parent.parent_property = 'parent'
>>> Child.parent_property
'parent'
>>> Child.parent_property = 'child'
>>> Parent.parent_property
'parent'
>>> Child.parent_property
'child'
>>> Child.child_property = 'child'
>>> Parent.child_property
Traceback (most recent call last):
...
AttributeError: ... 'Parent' has no attribute 'child_property'
@@ -0,0 +1,32 @@
import builtins
import inspect
import pprint
import re
def pp(*args, **kwargs):
'''PrettyPrint function that prints the variable name when
available and pprints the data
>>> x = 10
>>> pp(x)
# x: 10
'''
# Fetch the current frame from the stack
frame = inspect.currentframe().f_back
# Prepare the frame info
frame_info = inspect.getframeinfo(frame)
# Walk through the lines of the function
for line in frame_info[3]:
# Search for the pp() function call with a fancy regexp
m = re.search(r'\bpp\s*\(\s*([^)]*)\s*\)', line)
if m:
print('# %s:' % m.group(1), end=' ')
break
pprint.pprint(*args, **kwargs)
builtins.pf = pprint.pformat
builtins.pp = pp
@@ -0,0 +1,14 @@
>>> list = list((1, 2, 3))
>>> list
[1, 2, 3]
>>> list((4, 5, 6))
Traceback (most recent call last):
...
TypeError: 'list' object is not callable
>>> import = 'Some import'
Traceback (most recent call last):
...
SyntaxError: invalid syntax
@@ -0,0 +1,35 @@
>>> dict_ = dict(a=123)
>>> set_ = set((456,))
>>> for key in dict_:
... del dict_[key]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
>>> for item in set_:
... set_.remove(item)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Set changed size during iteration
>>> list_ = list(range(10))
>>> list_
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for item in list_:
... print(list_.pop(0), end=', ')
0, 1, 2, 3, 4,
>>> list_
[5, 6, 7, 8, 9]
>>> list_ = list(range(10))
>>> for item in list(list_):
... print(list_.pop(0), end=', ')
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
@@ -0,0 +1,20 @@
>>> exception = None
>>> try:
... 1 / 0
... except ZeroDivisionError as exception:
... pass
>>> exception
Traceback (most recent call last):
...
NameError: name 'exception' is not defined
>>> try:
... 1 / 0
... except ZeroDivisionError as exception:
... new_exception = exception
>>> new_exception
ZeroDivisionError('division by zero')
@@ -0,0 +1,14 @@
>>> functions = [lambda: i for i in range(3)]
>>> for function in functions:
... print(function(), end=', ')
2, 2, 2,
>>> from functools import partial
>>> functions = [partial(lambda x: x, i) for i in range(3)]
>>> for function in functions:
... print(function(), end=', ')
0, 1, 2,
@@ -0,0 +1,9 @@
import T_28_circular_imports_b
class FileA:
pass
class FileC(T_28_circular_imports_b.FileB):
pass
@@ -0,0 +1,5 @@
import T_28_circular_imports_a
class FileB(T_28_circular_imports_a.FileA):
pass
@@ -0,0 +1,2 @@
class FileA:
pass
@@ -0,0 +1,5 @@
import T_29_circular_imports_a
class FileB(T_29_circular_imports_a.FileA):
pass
@@ -0,0 +1,5 @@
import T_29_circular_imports_b
class FileC(T_29_circular_imports_b.FileB):
pass
@@ -0,0 +1,10 @@
>>> import importlib
>>> module_name = 'sys'
>>> attribute = 'version_info'
>>> module = importlib.import_module(module_name)
>>> module
<module 'sys' (built-in)>
>>> getattr(module, attribute).major
3
View File
+7
View File
@@ -0,0 +1,7 @@
import sys
import pathlib
# Little hack to add the current directory to sys.path so we can
# find the imports
path = pathlib.Path(__file__).parent
sys.path.append(str(path.resolve()))