Mastering Python Second Edition Release Code
This commit is contained in:
commit
3223a43fe3
82
.gitignore
vendored
Normal file
82
.gitignore
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
# Pytest files and test output files
|
||||
.xprocess/
|
||||
*.txt
|
||||
/*.log
|
||||
/*.json
|
||||
*.lprof
|
||||
*.profile
|
||||
*.valgrind
|
||||
*.callgrind
|
||||
*.pickle
|
||||
*.sqlite
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.mypy_cache/
|
||||
.pytest_cache/
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
#Ipython Notebook
|
||||
.ipynb_checkpoints
|
||||
.vimrc
|
||||
.python-version
|
||||
*.cover
|
||||
|
||||
# Ignore Dask files
|
||||
dask-worker-space
|
||||
33
.travis.yml
Normal file
33
.travis.yml
Normal file
@ -0,0 +1,33 @@
|
||||
# Config file for automatic testing at travis-ci.org
|
||||
|
||||
sudo: false
|
||||
language: python
|
||||
|
||||
env:
|
||||
global:
|
||||
- PIP_WHEEL_DIR=$HOME/.wheels
|
||||
- PIP_FIND_LINKS=file://$PIP_WHEEL_DIR
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- python: '3.8'
|
||||
env: TOXENV=py38
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.wheels
|
||||
|
||||
# command to install dependencies, e.g. pip install -r requirements.txt
|
||||
install:
|
||||
- mkdir -p $PIP_WHEEL_DIR
|
||||
- pip wheel -r requirements.txt
|
||||
- pip install tox
|
||||
|
||||
script:
|
||||
- tox
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: change
|
||||
|
||||
5
CH_01_getting_started/README.rst
Normal file
5
CH_01_getting_started/README.rst
Normal file
@ -0,0 +1,5 @@
|
||||
Chapter 1, Getting started
|
||||
##############################################################################
|
||||
|
||||
| One Environment per Project introduces virtual Python environments using
|
||||
| virtualenv or venv to isolate the packages in your Python projects.
|
||||
53
CH_01_getting_started/T_00_poetry/poetry.lock
generated
Normal file
53
CH_01_getting_started/T_00_poetry/poetry.lock
generated
Normal file
@ -0,0 +1,53 @@
|
||||
[[package]]
|
||||
name = "progressbar2"
|
||||
version = "3.55.0"
|
||||
description = "A Python Progressbar library to provide visual (yet text based) progress to long running operations."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
python-utils = ">=2.3.0"
|
||||
six = "*"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=1.7.4)"]
|
||||
tests = ["flake8 (>=3.7.7)", "pytest (>=4.6.9)", "pytest-cov (>=2.6.1)", "freezegun (>=0.3.11)", "sphinx (>=1.8.5)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-utils"
|
||||
version = "2.5.6"
|
||||
description = "Python Utils is a module with some convenient utilities not included with the standard Python install"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
six = "*"
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "5da1cef67396e7888aff720cf71ea44c6291a7f61df8cec6ae2fd7a0d7d23075"
|
||||
|
||||
[metadata.files]
|
||||
progressbar2 = [
|
||||
{file = "progressbar2-3.55.0-py2.py3-none-any.whl", hash = "sha256:e98fee031da31ab9138fd8dd838ac80eafba82764eb75a43d25e3ca622f47d14"},
|
||||
{file = "progressbar2-3.55.0.tar.gz", hash = "sha256:86835d1f1a9317ab41aeb1da5e4184975e2306586839d66daf63067c102f8f04"},
|
||||
]
|
||||
python-utils = [
|
||||
{file = "python-utils-2.5.6.tar.gz", hash = "sha256:352d5b1febeebf9b3cdb9f3c87a3b26ef22d3c9e274a8ec1e7048ecd2fac4349"},
|
||||
{file = "python_utils-2.5.6-py2.py3-none-any.whl", hash = "sha256:18fbc1a1df9a9061e3059a48ebe5c8a66b654d688b0e3ecca8b339a7f168f208"},
|
||||
]
|
||||
six = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
15
CH_01_getting_started/T_00_poetry/pyproject.toml
Normal file
15
CH_01_getting_started/T_00_poetry/pyproject.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[tool.poetry]
|
||||
name = "t_00_poetry"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Rick van Hattem <Wolph@wol.ph>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
progressbar2 = "^3.1.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
0
CH_01_getting_started/__init__.py
Normal file
0
CH_01_getting_started/__init__.py
Normal file
3
CH_02_interactive_python/README.rst
Normal file
3
CH_02_interactive_python/README.rst
Normal file
@ -0,0 +1,3 @@
|
||||
Chapter 2, Interactive Python Shells
|
||||
##############################################################################
|
||||
|
||||
2
CH_02_interactive_python/T_00_the_python_interpreter.rst
Normal file
2
CH_02_interactive_python/T_00_the_python_interpreter.rst
Normal file
@ -0,0 +1,2 @@
|
||||
>>> 'Hello world!'
|
||||
'Hello world!'
|
||||
27
CH_02_interactive_python/T_01_modifying_the_interpreter.rst
Normal file
27
CH_02_interactive_python/T_01_modifying_the_interpreter.rst
Normal file
@ -0,0 +1,27 @@
|
||||
# Adding default imports
|
||||
|
||||
>>> from pprint import pprint as pp
|
||||
>>> from pprint import pformat as pf
|
||||
|
||||
>>> pp(dict(spam=0xA, eggs=0xB))
|
||||
{'eggs': 11, 'spam': 10}
|
||||
>>> pf(dict(spam=0xA, eggs=0xB))
|
||||
"{'eggs': 11, 'spam': 10}"
|
||||
|
||||
|
||||
# Modifying prompt
|
||||
|
||||
>>> if True:
|
||||
... print('Hello!')
|
||||
Hello!
|
||||
|
||||
>>> import sys
|
||||
|
||||
>>> sys.ps1 = '> '
|
||||
>>> sys.ps2 = '. '
|
||||
|
||||
# With modified prompt
|
||||
|
||||
> if True:
|
||||
. print('Hello!')
|
||||
Hello!
|
||||
76
CH_02_interactive_python/T_02_modifying_autocompletion.py
Normal file
76
CH_02_interactive_python/T_02_modifying_autocompletion.py
Normal file
@ -0,0 +1,76 @@
|
||||
import __main__
|
||||
import re
|
||||
import atexit
|
||||
import readline
|
||||
import rlcompleter
|
||||
|
||||
|
||||
class Completer(rlcompleter.Completer):
|
||||
|
||||
ITEM_RE = re.compile(r'(?P<expression>.+?)\[(?P<key>[^\[]*)')
|
||||
|
||||
def complete(self, text, state):
|
||||
# Init namespace. From `rlcompleter.Completer.complete`
|
||||
if self.use_main_ns:
|
||||
self.namespace = __main__.__dict__
|
||||
|
||||
# If we find a [, try and return the keys
|
||||
if '[' in text:
|
||||
# At state 0 we need to prefetch the matches, after
|
||||
# that we use the cached results
|
||||
if state == 0:
|
||||
self.matches = list(self.item_matches(text))
|
||||
|
||||
# Try and return the match if it exists
|
||||
try:
|
||||
return self.matches[state]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
# Fallback to the normal completion
|
||||
return super().complete(text, state)
|
||||
|
||||
def item_matches(self, text):
|
||||
# Look for the pattern expression[key
|
||||
match = self.ITEM_RE.match(text)
|
||||
if match:
|
||||
search_key = match.group('key').lstrip()
|
||||
expression = match.group('expression')
|
||||
|
||||
# Strip quotes from the key
|
||||
if search_key and search_key[0] in {"'", '"'}:
|
||||
search_key = search_key.strip(search_key[0])
|
||||
|
||||
# Fetch the object from the namespace
|
||||
object_ = eval(expression, self.namespace)
|
||||
|
||||
# Duck typing, check if we have a `keys()` attribute
|
||||
if hasattr(object_, 'keys'):
|
||||
# Fetch the keys by executing the `keys()` method
|
||||
# Can you guess where the bug is?
|
||||
keys = object_.keys()
|
||||
for i, key in enumerate(keys):
|
||||
# Limit to 25 items for safety, could be
|
||||
# infinite
|
||||
if i >= 25:
|
||||
break
|
||||
|
||||
# Only return matching results
|
||||
if key.startswith(search_key):
|
||||
yield f'{expression}[{key!r}]'
|
||||
|
||||
|
||||
# By default readline doesn't call the autocompleter for [ because
|
||||
# it's considered a delimiter. With a little bit of work we can
|
||||
# fix this however :)
|
||||
delims = readline.get_completer_delims()
|
||||
# Remove [, ' and " from the delimiters
|
||||
delims = delims.replace('[', '').replace('"', '').replace("'", '')
|
||||
# Set the delimiters
|
||||
readline.set_completer_delims(delims)
|
||||
|
||||
# Create and set the completer
|
||||
completer = Completer()
|
||||
readline.set_completer(completer.complete)
|
||||
# Add a cleanup call on Python exit
|
||||
atexit.register(lambda: readline.set_completer(None))
|
||||
15
CH_02_interactive_python/T_02_modifying_autocompletion.rst
Normal file
15
CH_02_interactive_python/T_02_modifying_autocompletion.rst
Normal file
@ -0,0 +1,15 @@
|
||||
>>> sandwich = dict(spam=2, eggs=1, sausage=1)
|
||||
>>> sandwich.<TAB> # doctest: +SKIP
|
||||
sandwich.clear( sandwich.fromkeys( sandwich.items( sandwich.pop(
|
||||
sandwich.setdefault( sandwich.values( sandwich.copy( sandwich.get(
|
||||
sandwich.keys( sandwich.popitem( sandwich.update(
|
||||
>>> sandwich[ # doctest: +SKIP
|
||||
|
||||
|
||||
>>> sandwich = dict(spam=2, eggs=1, sausage=1)
|
||||
>>> sandwich['<TAB> # doctest: +SKIP
|
||||
sandwich['eggs'] sandwich['sausage'] sandwich['spam']
|
||||
|
||||
>>> import T_02_modifying_autocompletion
|
||||
|
||||
>>> autocompletion = T_02_modifying_autocompletion
|
||||
25
CH_02_interactive_python/T_03_bpython_rewind.rst
Normal file
25
CH_02_interactive_python/T_03_bpython_rewind.rst
Normal file
@ -0,0 +1,25 @@
|
||||
Note: missing_ok was added in Python 3.8
|
||||
|
||||
>>> import pathlib
|
||||
|
||||
>>> pathlib.Path('bpython.txt').unlink(missing_ok=True)
|
||||
|
||||
>>> with open('bpython.txt', 'a') as fh:
|
||||
... fh.write('x')
|
||||
...
|
||||
1
|
||||
>>> with open('bpython.txt') as fh:
|
||||
... print(fh.read())
|
||||
...
|
||||
x
|
||||
>>> sandwich = dict(spam=2, eggs=1, sausage=1)
|
||||
|
||||
>>> with open('bpython.txt', 'a') as fh:
|
||||
... fh.write('x')
|
||||
...
|
||||
1
|
||||
>>> with open('bpython.txt') as fh:
|
||||
... print(fh.read())
|
||||
...
|
||||
xx
|
||||
>>>
|
||||
0
CH_02_interactive_python/__init__.py
Normal file
0
CH_02_interactive_python/__init__.py
Normal file
4
CH_02_interactive_python/bpython_reload.py
Normal file
4
CH_02_interactive_python/bpython_reload.py
Normal file
@ -0,0 +1,4 @@
|
||||
with open('reload.txt', 'a+') as fh:
|
||||
fh.write('x')
|
||||
fh.seek(0)
|
||||
print(fh.read())
|
||||
7
CH_02_interactive_python/conftest.py
Normal file
7
CH_02_interactive_python/conftest.py
Normal 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()))
|
||||
1
CH_02_interactive_python/reload.txt
Normal file
1
CH_02_interactive_python/reload.txt
Normal file
@ -0,0 +1 @@
|
||||
xxxxx
|
||||
3
CH_02_interactive_python/session_filename.ipy
Normal file
3
CH_02_interactive_python/session_filename.ipy
Normal file
@ -0,0 +1,3 @@
|
||||
# coding: utf-8
|
||||
|
||||
sandwich = dict(spam=2, eggs=1, sausage=1)
|
||||
4
CH_03_pythonic_syntax/README.rst
Normal file
4
CH_03_pythonic_syntax/README.rst
Normal 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.
|
||||
78
CH_03_pythonic_syntax/T_00_format_strings.rst
Normal file
78
CH_03_pythonic_syntax/T_00_format_strings.rst
Normal file
@ -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
CH_03_pythonic_syntax/T_01_pep20.rst
Normal file
22
CH_03_pythonic_syntax/T_01_pep20.rst
Normal 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!
|
||||
14
CH_03_pythonic_syntax/T_02_beautiful_is_better_than_ugly.rst
Normal file
14
CH_03_pythonic_syntax/T_02_beautiful_is_better_than_ugly.rst
Normal file
@ -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
|
||||
47
CH_03_pythonic_syntax/T_04_simple_is_better_than_complex.rst
Normal file
47
CH_03_pythonic_syntax/T_04_simple_is_better_than_complex.rst
Normal file
@ -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]
|
||||
27
CH_03_pythonic_syntax/T_05_flat_is_better_than_nested.rst
Normal file
27
CH_03_pythonic_syntax/T_05_flat_is_better_than_nested.rst
Normal file
@ -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
|
||||
11
CH_03_pythonic_syntax/T_06_sparse_is_better_than_dense.rst
Normal file
11
CH_03_pythonic_syntax/T_06_sparse_is_better_than_dense.rst
Normal file
@ -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
|
||||
32
CH_03_pythonic_syntax/T_07_readability_counts.rst
Normal file
32
CH_03_pythonic_syntax/T_07_readability_counts.rst
Normal file
@ -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
|
||||
|
||||
18
CH_03_pythonic_syntax/T_08_practicality_beats_purity.rst
Normal file
18
CH_03_pythonic_syntax/T_08_practicality_beats_purity.rst
Normal file
@ -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,
|
||||
... )
|
||||
|
||||
51
CH_03_pythonic_syntax/T_09_silent_errors.rst
Normal file
51
CH_03_pythonic_syntax/T_09_silent_errors.rst
Normal file
@ -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
CH_03_pythonic_syntax/T_10_ambiguity.rst
Normal file
7
CH_03_pythonic_syntax/T_10_ambiguity.rst
Normal 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')
|
||||
3
CH_03_pythonic_syntax/T_11_now_or_never.rst
Normal file
3
CH_03_pythonic_syntax/T_11_now_or_never.rst
Normal file
@ -0,0 +1,3 @@
|
||||
>>> import warnings
|
||||
|
||||
>>> warnings.warn('Something deprecated', DeprecationWarning)
|
||||
9
CH_03_pythonic_syntax/T_12_namespaces.rst
Normal file
9
CH_03_pythonic_syntax/T_12_namespaces.rst
Normal file
@ -0,0 +1,9 @@
|
||||
>>> from json import loads
|
||||
|
||||
>>> loads('{}')
|
||||
{}
|
||||
|
||||
>>> import json
|
||||
|
||||
>>> json.loads('{}')
|
||||
{}
|
||||
28
CH_03_pythonic_syntax/T_13_duck_typing.rst
Normal file
28
CH_03_pythonic_syntax/T_13_duck_typing.rst
Normal file
@ -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
|
||||
58
CH_03_pythonic_syntax/T_14_value_versus_identity.rst
Normal file
58
CH_03_pythonic_syntax/T_14_value_versus_identity.rst
Normal file
@ -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
CH_03_pythonic_syntax/T_15_loops.rst
Normal file
21
CH_03_pythonic_syntax/T_15_loops.rst
Normal 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
CH_03_pythonic_syntax/T_16_mccabe.py
Normal file
18
CH_03_pythonic_syntax/T_16_mccabe.py
Normal 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
CH_03_pythonic_syntax/T_17_mypy.py
Normal file
2
CH_03_pythonic_syntax/T_17_mypy.py
Normal file
@ -0,0 +1,2 @@
|
||||
some_number: int
|
||||
some_number = 'test'
|
||||
2
CH_03_pythonic_syntax/T_18_flake8.py
Normal file
2
CH_03_pythonic_syntax/T_18_flake8.py
Normal file
@ -0,0 +1,2 @@
|
||||
def spam(a,b,c):print(a,b+c)
|
||||
def eggs():pass
|
||||
182
CH_03_pythonic_syntax/T_19_match_statement.rst
Normal file
182
CH_03_pythonic_syntax/T_19_match_statement.rst
Normal file
@ -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
|
||||
18
CH_03_pythonic_syntax/T_20_global_variables.rst
Normal file
18
CH_03_pythonic_syntax/T_20_global_variables.rst
Normal file
@ -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
|
||||
|
||||
37
CH_03_pythonic_syntax/T_21_mutable_variables.rst
Normal file
37
CH_03_pythonic_syntax/T_21_mutable_variables.rst
Normal file
@ -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']
|
||||
47
CH_03_pythonic_syntax/T_22_class_properties.rst
Normal file
47
CH_03_pythonic_syntax/T_22_class_properties.rst
Normal file
@ -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'
|
||||
32
CH_03_pythonic_syntax/T_23_prettyprint_builtin.py
Normal file
32
CH_03_pythonic_syntax/T_23_prettyprint_builtin.py
Normal file
@ -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
|
||||
14
CH_03_pythonic_syntax/T_24_overwriting_built-ins.rst
Normal file
14
CH_03_pythonic_syntax/T_24_overwriting_built-ins.rst
Normal file
@ -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
|
||||
|
||||
35
CH_03_pythonic_syntax/T_25_modify_while_iterating.rst
Normal file
35
CH_03_pythonic_syntax/T_25_modify_while_iterating.rst
Normal file
@ -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,
|
||||
20
CH_03_pythonic_syntax/T_26_catching_exceptions.rst
Normal file
20
CH_03_pythonic_syntax/T_26_catching_exceptions.rst
Normal file
@ -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')
|
||||
14
CH_03_pythonic_syntax/T_27_late_binding.rst
Normal file
14
CH_03_pythonic_syntax/T_27_late_binding.rst
Normal file
@ -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,
|
||||
9
CH_03_pythonic_syntax/T_28_circular_imports_a.py
Normal file
9
CH_03_pythonic_syntax/T_28_circular_imports_a.py
Normal file
@ -0,0 +1,9 @@
|
||||
import T_28_circular_imports_b
|
||||
|
||||
|
||||
class FileA:
|
||||
pass
|
||||
|
||||
|
||||
class FileC(T_28_circular_imports_b.FileB):
|
||||
pass
|
||||
5
CH_03_pythonic_syntax/T_28_circular_imports_b.py
Normal file
5
CH_03_pythonic_syntax/T_28_circular_imports_b.py
Normal file
@ -0,0 +1,5 @@
|
||||
import T_28_circular_imports_a
|
||||
|
||||
|
||||
class FileB(T_28_circular_imports_a.FileA):
|
||||
pass
|
||||
2
CH_03_pythonic_syntax/T_29_circular_imports_a.py
Normal file
2
CH_03_pythonic_syntax/T_29_circular_imports_a.py
Normal file
@ -0,0 +1,2 @@
|
||||
class FileA:
|
||||
pass
|
||||
5
CH_03_pythonic_syntax/T_29_circular_imports_b.py
Normal file
5
CH_03_pythonic_syntax/T_29_circular_imports_b.py
Normal file
@ -0,0 +1,5 @@
|
||||
import T_29_circular_imports_a
|
||||
|
||||
|
||||
class FileB(T_29_circular_imports_a.FileA):
|
||||
pass
|
||||
5
CH_03_pythonic_syntax/T_29_circular_imports_c.py
Normal file
5
CH_03_pythonic_syntax/T_29_circular_imports_c.py
Normal file
@ -0,0 +1,5 @@
|
||||
import T_29_circular_imports_b
|
||||
|
||||
|
||||
class FileC(T_29_circular_imports_b.FileB):
|
||||
pass
|
||||
10
CH_03_pythonic_syntax/T_30_dynamic_imports.rst
Normal file
10
CH_03_pythonic_syntax/T_30_dynamic_imports.rst
Normal file
@ -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
|
||||
0
CH_03_pythonic_syntax/__init__.py
Normal file
0
CH_03_pythonic_syntax/__init__.py
Normal file
7
CH_03_pythonic_syntax/conftest.py
Normal file
7
CH_03_pythonic_syntax/conftest.py
Normal 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()))
|
||||
4
CH_04_design_patterns/README.rst
Normal file
4
CH_04_design_patterns/README.rst
Normal 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.
|
||||
34
CH_04_design_patterns/T_00_time_complexity_big_o.rst
Normal file
34
CH_04_design_patterns/T_00_time_complexity_big_o.rst
Normal file
@ -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
CH_04_design_patterns/T_01_list.rst
Normal file
89
CH_04_design_patterns/T_01_list.rst
Normal 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
CH_04_design_patterns/T_02_dict.rst
Normal file
48
CH_04_design_patterns/T_02_dict.rst
Normal 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
CH_04_design_patterns/T_03_set.rst
Normal file
38
CH_04_design_patterns/T_03_set.rst
Normal 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
CH_04_design_patterns/T_04_tuple.rst
Normal file
99
CH_04_design_patterns/T_04_tuple.rst
Normal 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'
|
||||
59
CH_04_design_patterns/T_05_dataclasses.rst
Normal file
59
CH_04_design_patterns/T_05_dataclasses.rst
Normal file
@ -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
CH_04_design_patterns/T_06_chainmap.rst
Normal file
74
CH_04_design_patterns/T_06_chainmap.rst
Normal 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
|
||||
|
||||
93
CH_04_design_patterns/T_07_defaultdict.rst
Normal file
93
CH_04_design_patterns/T_07_defaultdict.rst
Normal file
@ -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
CH_04_design_patterns/T_08_enum.rst
Normal file
60
CH_04_design_patterns/T_08_enum.rst
Normal 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
CH_04_design_patterns/T_09_heapq.rst
Normal file
29
CH_04_design_patterns/T_09_heapq.rst
Normal 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
CH_04_design_patterns/T_10_bisect.rst
Normal file
99
CH_04_design_patterns/T_10_bisect.rst
Normal 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]
|
||||
37
CH_04_design_patterns/T_11_borg_and_singleton.rst
Normal file
37
CH_04_design_patterns/T_11_borg_and_singleton.rst
Normal file
@ -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
|
||||
23
CH_04_design_patterns/T_12_getters_and_setters.rst
Normal file
23
CH_04_design_patterns/T_12_getters_and_setters.rst
Normal file
@ -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
CH_04_design_patterns/T_13_dict_union.rst
Normal file
22
CH_04_design_patterns/T_13_dict_union.rst
Normal 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}
|
||||
0
CH_04_design_patterns/__init__.py
Normal file
0
CH_04_design_patterns/__init__.py
Normal file
4
CH_05_functional_programming/README.rst
Normal file
4
CH_05_functional_programming/README.rst
Normal 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.
|
||||
17
CH_05_functional_programming/T_00_intro.rst
Normal file
17
CH_05_functional_programming/T_00_intro.rst
Normal file
@ -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]
|
||||
129
CH_05_functional_programming/T_01_list_comprehensions.rst
Normal file
129
CH_05_functional_programming/T_01_list_comprehensions.rst
Normal file
@ -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]]
|
||||
|
||||
10
CH_05_functional_programming/T_02_dict_comprehensions.rst
Normal file
10
CH_05_functional_programming/T_02_dict_comprehensions.rst
Normal file
@ -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]}
|
||||
5
CH_05_functional_programming/T_03_set_comprehensions.rst
Normal file
5
CH_05_functional_programming/T_03_set_comprehensions.rst
Normal file
@ -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}
|
||||
25
CH_05_functional_programming/T_04_lambda_functions.rst
Normal file
25
CH_05_functional_programming/T_04_lambda_functions.rst
Normal file
@ -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
|
||||
|
||||
54
CH_05_functional_programming/T_05_y_combinator.rst
Normal file
54
CH_05_functional_programming/T_05_y_combinator.rst
Normal file
@ -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]
|
||||
|
||||
50
CH_05_functional_programming/T_06_partial.rst
Normal file
50
CH_05_functional_programming/T_06_partial.rst
Normal file
@ -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__
|
||||
|
||||
86
CH_05_functional_programming/T_07_factorial.rst
Normal file
86
CH_05_functional_programming/T_07_factorial.rst
Normal file
@ -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
|
||||
|
||||
73
CH_05_functional_programming/T_08_trees.rst
Normal file
73
CH_05_functional_programming/T_08_trees.rst
Normal file
@ -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,
|
||||
... )
|
||||
|
||||
9
CH_05_functional_programming/T_09_accumulate.rst
Normal file
9
CH_05_functional_programming/T_09_accumulate.rst
Normal file
@ -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]
|
||||
|
||||
13
CH_05_functional_programming/T_10_chain.rst
Normal file
13
CH_05_functional_programming/T_10_chain.rst
Normal file
@ -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]
|
||||
24
CH_05_functional_programming/T_11_compress.rst
Normal file
24
CH_05_functional_programming/T_11_compress.rst
Normal file
@ -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]
|
||||
11
CH_05_functional_programming/T_13_count.rst
Normal file
11
CH_05_functional_programming/T_13_count.rst
Normal file
@ -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]
|
||||
|
||||
34
CH_05_functional_programming/T_14_groupby.rst
Normal file
34
CH_05_functional_programming/T_14_groupby.rst
Normal file
@ -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']
|
||||
0
CH_05_functional_programming/__init__.py
Normal file
0
CH_05_functional_programming/__init__.py
Normal file
4
CH_06_decorators/README.rst
Normal file
4
CH_06_decorators/README.rst
Normal file
@ -0,0 +1,4 @@
|
||||
Chapter 5, Decorators
|
||||
##############################################################################
|
||||
|
||||
| Enabling Code Reuse by Decorating explains not only how to create your own function/class decorators but also how internal decorators such as property, staticmethod and classmethod function.
|
||||
39
CH_06_decorators/T_00_decorating_functions.rst
Normal file
39
CH_06_decorators/T_00_decorating_functions.rst
Normal file
@ -0,0 +1,39 @@
|
||||
>>> def decorator(function):
|
||||
... return function
|
||||
|
||||
>>> def add(a, b):
|
||||
... return a + b
|
||||
|
||||
>>> add = decorator(add)
|
||||
|
||||
|
||||
>>> @decorator
|
||||
... def add(a, b):
|
||||
... return a + b
|
||||
|
||||
|
||||
>>> import functools
|
||||
|
||||
>>> def decorator(function):
|
||||
... # This decorator makes sure we mimic the wrapped function
|
||||
... @functools.wraps(function)
|
||||
... def _decorator(a, b):
|
||||
... # Pass the modified arguments to the function
|
||||
... result = function(a, b + 5)
|
||||
...
|
||||
... # Logging the function call
|
||||
... name = function.__name__
|
||||
... print(f'{name}(a={a}, b={b}): {result}')
|
||||
...
|
||||
... # Return a modified result
|
||||
... return result + 4
|
||||
...
|
||||
... return _decorator
|
||||
|
||||
>>> @decorator
|
||||
... def func(a, b):
|
||||
... return a + b
|
||||
|
||||
>>> func(1, 2)
|
||||
func(a=1, b=2): 8
|
||||
12
|
||||
82
CH_06_decorators/T_01_generic_decorators.rst
Normal file
82
CH_06_decorators/T_01_generic_decorators.rst
Normal file
@ -0,0 +1,82 @@
|
||||
>>> import functools
|
||||
|
||||
>>> def decorator(function):
|
||||
... @functools.wraps(function)
|
||||
... def _decorator(*args, **kwargs):
|
||||
... a, b = args
|
||||
... return function(a, b + 5)
|
||||
...
|
||||
... return _decorator
|
||||
|
||||
>>> @decorator
|
||||
... def func(a, b):
|
||||
... return a + b
|
||||
|
||||
>>> func(1, 2)
|
||||
8
|
||||
|
||||
>>> func(a=1, b=2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: not enough values to unpack (expected 2, got 0)
|
||||
|
||||
|
||||
>>> def add(a, b, /):
|
||||
... return a + b
|
||||
|
||||
>>> add(a=1, b=2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: add() got some positional-only arguments passed ...
|
||||
|
||||
|
||||
|
||||
>>> def add(*, a, b):
|
||||
... return a + b
|
||||
|
||||
>>> add(1, 2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: add() takes 0 positional arguments but 2 were given
|
||||
|
||||
|
||||
|
||||
>>> import inspect
|
||||
>>> import functools
|
||||
|
||||
>>> def decorator(function):
|
||||
... # Use the inspect module to get function signature. More
|
||||
... # about this in the logging chapter
|
||||
... signature = inspect.signature(function)
|
||||
...
|
||||
... @functools.wraps(function)
|
||||
... def _decorator(*args, **kwargs):
|
||||
... # Bind the arguments to the given *args and **kwargs.
|
||||
... # If you want to make arguments optional use
|
||||
... # signature.bind_partial instead.
|
||||
... bound = signature.bind(*args, **kwargs)
|
||||
...
|
||||
... # Apply the defaults so b is always filled
|
||||
... bound.apply_defaults()
|
||||
...
|
||||
... # Extract the filled arguments. If the amount of
|
||||
... # arguments is still expected to be fixed you can use
|
||||
... # tuple unpacking: `a, b = bound.arguments.values()`
|
||||
... a = bound.arguments['a']
|
||||
... b = bound.arguments['b']
|
||||
... return function(a, b + 5)
|
||||
...
|
||||
... return _decorator
|
||||
|
||||
>>> @decorator
|
||||
... def func(a, b=3):
|
||||
... return a + b
|
||||
|
||||
>>> func(1, 2)
|
||||
8
|
||||
|
||||
>>> func(a=1, b=2)
|
||||
8
|
||||
|
||||
>>> func(a=1)
|
||||
9
|
||||
43
CH_06_decorators/T_02_functools_wraps.rst
Normal file
43
CH_06_decorators/T_02_functools_wraps.rst
Normal file
@ -0,0 +1,43 @@
|
||||
>>> def decorator(function):
|
||||
... def _decorator(*args, **kwargs):
|
||||
... return function(*args, **kwargs)
|
||||
... return _decorator
|
||||
|
||||
>>> @decorator
|
||||
... def add(a, b):
|
||||
... '''Add a and b'''
|
||||
... return a + b
|
||||
|
||||
>>> help(add)
|
||||
Help on function _decorator in module ...:
|
||||
<BLANKLINE>
|
||||
_decorator(*args, **kwargs)
|
||||
<BLANKLINE>
|
||||
|
||||
>>> add.__name__
|
||||
'_decorator'
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> import functools
|
||||
|
||||
>>> def decorator(function):
|
||||
... @functools.wraps(function)
|
||||
... def _decorator(*args, **kwargs):
|
||||
... return function(*args, **kwargs)
|
||||
... return _decorator
|
||||
|
||||
>>> @decorator
|
||||
... def add(a, b):
|
||||
... '''Add a and b'''
|
||||
... return a + b
|
||||
|
||||
>>> help(add)
|
||||
Help on function add in module ...:
|
||||
<BLANKLINE>
|
||||
add(a, b)
|
||||
Add a and b
|
||||
<BLANKLINE>
|
||||
|
||||
>>> add.__name__
|
||||
'add'
|
||||
30
CH_06_decorators/T_03_chaining_decorators.rst
Normal file
30
CH_06_decorators/T_03_chaining_decorators.rst
Normal file
@ -0,0 +1,30 @@
|
||||
>>> import functools
|
||||
|
||||
>>> def track(function=None, label=None):
|
||||
... # Trick to add an optional argument to our decorator
|
||||
... if label and not function:
|
||||
... return functools.partial(track, label=label)
|
||||
...
|
||||
... print(f'initializing {label}')
|
||||
...
|
||||
... @functools.wraps(function)
|
||||
... def _track(*args, **kwargs):
|
||||
... print(f'calling {label}')
|
||||
... function(*args, **kwargs)
|
||||
... print(f'called {label}')
|
||||
...
|
||||
... return _track
|
||||
|
||||
>>> @track(label='outer')
|
||||
... @track(label='inner')
|
||||
... def func():
|
||||
... print('func')
|
||||
initializing inner
|
||||
initializing outer
|
||||
|
||||
>>> func()
|
||||
calling outer
|
||||
calling inner
|
||||
func
|
||||
called inner
|
||||
called outer
|
||||
35
CH_06_decorators/T_04_registering_decorators.rst
Normal file
35
CH_06_decorators/T_04_registering_decorators.rst
Normal file
@ -0,0 +1,35 @@
|
||||
>>> import collections
|
||||
|
||||
|
||||
>>> class EventRegistry:
|
||||
... def __init__(self):
|
||||
... self.registry = collections.defaultdict(list)
|
||||
...
|
||||
... def on(self, *events):
|
||||
... def _on(function):
|
||||
... for event in events:
|
||||
... self.registry[event].append(function)
|
||||
... return function
|
||||
...
|
||||
... return _on
|
||||
...
|
||||
... def fire(self, event, *args, **kwargs):
|
||||
... for function in self.registry[event]:
|
||||
... function(*args, **kwargs)
|
||||
|
||||
>>> events = EventRegistry()
|
||||
|
||||
>>> @events.on('success', 'error')
|
||||
... def teardown(value):
|
||||
... print(f'Tearing down got: {value}')
|
||||
|
||||
>>> @events.on('success')
|
||||
... def success(value):
|
||||
... print(f'Successfully executed: {value}')
|
||||
|
||||
>>> events.fire('non-existing', 'nothing to see here')
|
||||
>>> events.fire('error', 'Oops, some error here')
|
||||
Tearing down got: Oops, some error here
|
||||
>>> events.fire('success', 'Everything is fine')
|
||||
Tearing down got: Everything is fine
|
||||
Successfully executed: Everything is fine
|
||||
88
CH_06_decorators/T_05_memoization.rst
Normal file
88
CH_06_decorators/T_05_memoization.rst
Normal file
@ -0,0 +1,88 @@
|
||||
>>> import functools
|
||||
|
||||
>>> def memoize(function):
|
||||
... # Store the cache as attribute of the function so we can
|
||||
... # apply the decorator to multiple functions without
|
||||
... # sharing the cache.
|
||||
... function.cache = dict()
|
||||
...
|
||||
... @functools.wraps(function)
|
||||
... def _memoize(*args):
|
||||
... # If the cache is not available, call the function
|
||||
... # Note that all args need to be hashable
|
||||
... if args not in function.cache:
|
||||
... function.cache[args] = function(*args)
|
||||
... return function.cache[args]
|
||||
... return _memoize
|
||||
|
||||
>>> @memoize
|
||||
... def fibonacci(n):
|
||||
... if n < 2:
|
||||
... return n
|
||||
... else:
|
||||
... return fibonacci(n - 1) + fibonacci(n - 2)
|
||||
|
||||
>>> for i in range(1, 7):
|
||||
... print(f'fibonacci {i}: {fibonacci(i)}')
|
||||
fibonacci 1: 1
|
||||
fibonacci 2: 1
|
||||
fibonacci 3: 2
|
||||
fibonacci 4: 3
|
||||
fibonacci 5: 5
|
||||
fibonacci 6: 8
|
||||
|
||||
>>> fibonacci.__wrapped__.cache
|
||||
{(1,): 1, (0,): 0, (2,): 1, (3,): 2, (4,): 3, (5,): 5, (6,): 8}
|
||||
|
||||
# It breaks keyword arguments:
|
||||
|
||||
>>> fibonacci(n=2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: ... got an unexpected keyword argument 'n'
|
||||
|
||||
# Unhashable types don't work as dict keys:
|
||||
|
||||
>>> fibonacci([123])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: unhashable type: 'list'
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> import functools
|
||||
|
||||
# Create a simple call counting decorator
|
||||
|
||||
>>> def counter(function):
|
||||
... function.calls = 0
|
||||
... @functools.wraps(function)
|
||||
... def _counter(*args, **kwargs):
|
||||
... function.calls += 1
|
||||
... return function(*args, **kwargs)
|
||||
... return _counter
|
||||
|
||||
# Create a LRU cache with size 3
|
||||
|
||||
>>> @functools.lru_cache(maxsize=3)
|
||||
... @counter
|
||||
... def fibonacci(n):
|
||||
... if n < 2:
|
||||
... return n
|
||||
... else:
|
||||
... return fibonacci(n - 1) + fibonacci(n - 2)
|
||||
|
||||
>>> fibonacci(100)
|
||||
354224848179261915075
|
||||
|
||||
# The LRU cache offers some useful statistics
|
||||
|
||||
>>> fibonacci.cache_info()
|
||||
CacheInfo(hits=98, misses=101, maxsize=3, currsize=3)
|
||||
|
||||
# The result from our counter function which is now wrapped both by
|
||||
our counter and the cache
|
||||
|
||||
>>> fibonacci.__wrapped__.__wrapped__.calls
|
||||
101
|
||||
36
CH_06_decorators/T_06_optional_arguments.rst
Normal file
36
CH_06_decorators/T_06_optional_arguments.rst
Normal file
@ -0,0 +1,36 @@
|
||||
>>> import functools
|
||||
|
||||
>>> def add(function=None, add_n=0):
|
||||
... # function is not callable so it's probably `add_n`
|
||||
... if not callable(function):
|
||||
... # Test to make sure we don't pass `None` as `add_n`
|
||||
... if function is not None:
|
||||
... add_n = function
|
||||
... return functools.partial(add, add_n=add_n)
|
||||
...
|
||||
... @functools.wraps(function)
|
||||
... def _add(n):
|
||||
... return function(n) + add_n
|
||||
...
|
||||
... return _add
|
||||
|
||||
>>> @add
|
||||
... def add_zero(n):
|
||||
... return n
|
||||
|
||||
>>> @add(1)
|
||||
... def add_one(n):
|
||||
... return n
|
||||
|
||||
>>> @add(add_n=2)
|
||||
... def add_two(n):
|
||||
... return n
|
||||
|
||||
>>> add_zero(5)
|
||||
5
|
||||
|
||||
>>> add_one(5)
|
||||
6
|
||||
|
||||
>>> add_two(5)
|
||||
7
|
||||
25
CH_06_decorators/T_07_decorators_using_classes.rst
Normal file
25
CH_06_decorators/T_07_decorators_using_classes.rst
Normal file
@ -0,0 +1,25 @@
|
||||
>>> import functools
|
||||
|
||||
>>> class Debug(object):
|
||||
...
|
||||
... def __init__(self, function):
|
||||
... self.function = function
|
||||
... # functools.wraps for classes
|
||||
... functools.update_wrapper(self, function)
|
||||
...
|
||||
... def __call__(self, *args, **kwargs):
|
||||
... output = self.function(*args, **kwargs)
|
||||
... name = self.function.__name__
|
||||
... print(f'{name}({args!r}, {kwargs!r}): {output!r}')
|
||||
... return output
|
||||
|
||||
|
||||
>>> @Debug
|
||||
... def add(a, b=0):
|
||||
... return a + b
|
||||
...
|
||||
>>> output = add(3)
|
||||
add((3,), {}): 3
|
||||
|
||||
>>> output = add(a=4, b=2)
|
||||
add((), {'a': 4, 'b': 2}): 6
|
||||
21
CH_06_decorators/T_08_decorating_class_functions.rst
Normal file
21
CH_06_decorators/T_08_decorating_class_functions.rst
Normal file
@ -0,0 +1,21 @@
|
||||
>>> import functools
|
||||
|
||||
|
||||
>>> def plus_one(function):
|
||||
... @functools.wraps(function)
|
||||
... def _plus_one(self, n, *args):
|
||||
... return function(self, n + 1, *args)
|
||||
... return _plus_one
|
||||
|
||||
|
||||
>>> class Adder(object):
|
||||
... @plus_one
|
||||
... def add(self, a, b=0):
|
||||
... return a + b
|
||||
|
||||
|
||||
>>> adder = Adder()
|
||||
>>> adder.add(0)
|
||||
1
|
||||
>>> adder.add(3, 4)
|
||||
8
|
||||
146
CH_06_decorators/T_09_classmethod_and_staticmethod.rst
Normal file
146
CH_06_decorators/T_09_classmethod_and_staticmethod.rst
Normal file
@ -0,0 +1,146 @@
|
||||
>>> import pprint
|
||||
|
||||
|
||||
>>> class Spam(object):
|
||||
...
|
||||
... def some_instancemethod(self, *args, **kwargs):
|
||||
... pprint.pprint(locals(), width=60)
|
||||
...
|
||||
... @classmethod
|
||||
... def some_classmethod(cls, *args, **kwargs):
|
||||
... pprint.pprint(locals(), width=60)
|
||||
...
|
||||
... @staticmethod
|
||||
... def some_staticmethod(*args, **kwargs):
|
||||
... pprint.pprint(locals(), width=60)
|
||||
|
||||
# Create an instance so we can compare the difference between
|
||||
executions with and without instances easily
|
||||
|
||||
>>> spam = Spam()
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# With an instance (note the lowercase spam)
|
||||
|
||||
>>> spam.some_instancemethod(1, 2, a=3, b=4)
|
||||
{'args': (1, 2),
|
||||
'kwargs': {'a': 3, 'b': 4},
|
||||
'self': <__main__.Spam object at ...>}
|
||||
|
||||
# Without an instance (note the capitalized Spam)
|
||||
|
||||
>>> Spam.some_instancemethod()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: ...some_instancemethod() missing ... argument: 'self'
|
||||
|
||||
# But what if we add parameters? Be very careful with these!
|
||||
Our first argument is now used as an argument, this can give
|
||||
very strange and unexpected errors
|
||||
|
||||
>>> Spam.some_instancemethod(1, 2, a=3, b=4)
|
||||
{'args': (2,), 'kwargs': {'a': 3, 'b': 4}, 'self': 1}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# Classmethods are expectedly identical
|
||||
|
||||
>>> spam.some_classmethod(1, 2, a=3, b=4)
|
||||
{'args': (1, 2),
|
||||
'cls': <class '__main__.Spam'>,
|
||||
'kwargs': {'a': 3, 'b': 4}}
|
||||
|
||||
>>> Spam.some_classmethod()
|
||||
{'args': (), 'cls': <class '__main__.Spam'>, 'kwargs': {}}
|
||||
|
||||
>>> Spam.some_classmethod(1, 2, a=3, b=4)
|
||||
{'args': (1, 2),
|
||||
'cls': <class '__main__.Spam'>,
|
||||
'kwargs': {'a': 3, 'b': 4}}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# Staticmethods are also identical
|
||||
|
||||
>>> spam.some_staticmethod(1, 2, a=3, b=4)
|
||||
{'args': (1, 2), 'kwargs': {'a': 3, 'b': 4}}
|
||||
|
||||
>>> Spam.some_staticmethod()
|
||||
{'args': (), 'kwargs': {}}
|
||||
|
||||
>>> Spam.some_staticmethod(1, 2, a=3, b=4)
|
||||
{'args': (1, 2), 'kwargs': {'a': 3, 'b': 4}}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> class Spam:
|
||||
...
|
||||
... def __init__(self, spam=1):
|
||||
... self.spam = spam
|
||||
...
|
||||
... def __get__(self, instance, cls):
|
||||
... return self.spam + instance.eggs
|
||||
...
|
||||
... def __set__(self, instance, value):
|
||||
... instance.eggs = value - self.spam
|
||||
|
||||
>>> class Sandwich:
|
||||
...
|
||||
... spam = Spam(5)
|
||||
...
|
||||
... def __init__(self, eggs):
|
||||
... self.eggs = eggs
|
||||
|
||||
>>> sandwich = Sandwich(1)
|
||||
>>> sandwich.eggs
|
||||
1
|
||||
>>> sandwich.spam
|
||||
6
|
||||
|
||||
>>> sandwich.eggs = 10
|
||||
>>> sandwich.spam
|
||||
15
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> import functools
|
||||
|
||||
>>> class ClassMethod(object):
|
||||
... def __init__(self, method):
|
||||
... self.method = method
|
||||
...
|
||||
... def __get__(self, instance, cls):
|
||||
... @functools.wraps(self.method)
|
||||
... def method(*args, **kwargs):
|
||||
... return self.method(cls, *args, **kwargs)
|
||||
... return method
|
||||
|
||||
>>> class StaticMethod(object):
|
||||
... def __init__(self, method):
|
||||
... self.method = method
|
||||
...
|
||||
... def __get__(self, instance, cls):
|
||||
... return self.method
|
||||
|
||||
>>> class Sandwich:
|
||||
... spam = 'class'
|
||||
...
|
||||
... def __init__(self, spam):
|
||||
... self.spam = spam
|
||||
...
|
||||
... @ClassMethod
|
||||
... def some_classmethod(cls, arg):
|
||||
... return cls.spam, arg
|
||||
...
|
||||
... @StaticMethod
|
||||
... def some_staticmethod(arg):
|
||||
... return Sandwich.spam, arg
|
||||
|
||||
>>> sandwich = Sandwich('instance')
|
||||
>>> sandwich.spam
|
||||
'instance'
|
||||
>>> sandwich.some_classmethod('argument')
|
||||
('class', 'argument')
|
||||
>>> sandwich.some_staticmethod('argument')
|
||||
('class', 'argument')
|
||||
137
CH_06_decorators/T_10_properties.rst
Normal file
137
CH_06_decorators/T_10_properties.rst
Normal file
@ -0,0 +1,137 @@
|
||||
>>> import functools
|
||||
|
||||
>>> class Sandwich:
|
||||
... def get_eggs(self):
|
||||
... print('getting eggs')
|
||||
... return self._eggs
|
||||
...
|
||||
... def set_eggs(self, eggs):
|
||||
... print('setting eggs to %s' % eggs)
|
||||
... self._eggs = eggs
|
||||
...
|
||||
... def delete_eggs(self):
|
||||
... print('deleting eggs')
|
||||
... del self._eggs
|
||||
...
|
||||
... eggs = property(get_eggs, set_eggs, delete_eggs)
|
||||
...
|
||||
... @property
|
||||
... def spam(self):
|
||||
... print('getting spam')
|
||||
... return self._spam
|
||||
...
|
||||
... @spam.setter
|
||||
... def spam(self, spam):
|
||||
... print('setting spam to %s' % spam)
|
||||
... self._spam = spam
|
||||
...
|
||||
... @spam.deleter
|
||||
... def spam(self):
|
||||
... print('deleting spam')
|
||||
... del self._spam
|
||||
...
|
||||
... @functools.cached_property
|
||||
... def bacon(self):
|
||||
... print('getting bacon')
|
||||
... return 'bacon!'
|
||||
|
||||
>>> sandwich = Sandwich()
|
||||
>>> sandwich.eggs = 123
|
||||
setting eggs to 123
|
||||
>>> sandwich.eggs
|
||||
getting eggs
|
||||
123
|
||||
>>> del sandwich.eggs
|
||||
deleting eggs
|
||||
>>> sandwich.bacon
|
||||
getting bacon
|
||||
'bacon!'
|
||||
>>> sandwich.bacon
|
||||
'bacon!'
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> class Property(object):
|
||||
... def __init__(self, fget=None, fset=None, fdel=None):
|
||||
... self.fget = fget
|
||||
... self.fset = fset
|
||||
... self.fdel = fdel
|
||||
...
|
||||
... def __get__(self, instance, cls):
|
||||
... if instance is None:
|
||||
... # Redirect class (not instance) properties to self
|
||||
... return self
|
||||
... elif self.fget:
|
||||
... return self.fget(instance)
|
||||
...
|
||||
... def __set__(self, instance, value):
|
||||
... self.fset(instance, value)
|
||||
...
|
||||
... def __delete__(self, instance):
|
||||
... self.fdel(instance)
|
||||
...
|
||||
... def getter(self, fget):
|
||||
... return Property(fget, self.fset, self.fdel)
|
||||
...
|
||||
... def setter(self, fset):
|
||||
... return Property(self.fget, fset, self.fdel)
|
||||
...
|
||||
... def deleter(self, fdel):
|
||||
... return Property(self.fget, self.fset, fdel)
|
||||
|
||||
>>> class Sandwich:
|
||||
... @Property
|
||||
... def eggs(self):
|
||||
... return self._eggs
|
||||
...
|
||||
... @eggs.setter
|
||||
... def eggs(self, value):
|
||||
... self._eggs = value
|
||||
...
|
||||
... @eggs.deleter
|
||||
... def eggs(self):
|
||||
... del self._eggs
|
||||
|
||||
>>> sandwich = Sandwich()
|
||||
>>> sandwich.eggs = 5
|
||||
>>> sandwich.eggs
|
||||
5
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> class Sandwich(object):
|
||||
... def __init__(self):
|
||||
... self.registry = {}
|
||||
...
|
||||
... def __getattr__(self, key):
|
||||
... print('Getting %r' % key)
|
||||
... return self.registry.get(key, 'Undefined')
|
||||
...
|
||||
... def __setattr__(self, key, value):
|
||||
... if key == 'registry':
|
||||
... object.__setattr__(self, key, value)
|
||||
... else:
|
||||
... print('Setting %r to %r' % (key, value))
|
||||
... self.registry[key] = value
|
||||
...
|
||||
... def __delattr__(self, key):
|
||||
... print('Deleting %r' % key)
|
||||
... del self.registry[key]
|
||||
|
||||
|
||||
>>> sandwich = Sandwich()
|
||||
|
||||
>>> sandwich.a
|
||||
Getting 'a'
|
||||
'Undefined'
|
||||
|
||||
>>> sandwich.a = 1
|
||||
Setting 'a' to 1
|
||||
|
||||
>>> sandwich.a
|
||||
Getting 'a'
|
||||
1
|
||||
|
||||
>>> del sandwich.a
|
||||
Deleting 'a'
|
||||
26
CH_06_decorators/T_11_singletons.rst
Normal file
26
CH_06_decorators/T_11_singletons.rst
Normal file
@ -0,0 +1,26 @@
|
||||
>>> import functools
|
||||
|
||||
>>> def singleton(cls):
|
||||
... instances = dict()
|
||||
... @functools.wraps(cls)
|
||||
... def _singleton(*args, **kwargs):
|
||||
... if cls not in instances:
|
||||
... instances[cls] = cls(*args, **kwargs)
|
||||
... return instances[cls]
|
||||
... return _singleton
|
||||
|
||||
>>> @singleton
|
||||
... class SomeSingleton(object):
|
||||
... def __init__(self):
|
||||
... print('Executing init')
|
||||
|
||||
>>> a = SomeSingleton()
|
||||
Executing init
|
||||
>>> b = SomeSingleton()
|
||||
|
||||
>>> a is b
|
||||
True
|
||||
|
||||
>>> a.x = 123
|
||||
>>> b.x
|
||||
123
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user