Mastering Python Second Edition Release Code
This commit is contained in:
+82
@@ -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
@@ -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
|
||||||
|
|
||||||
@@ -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
@@ -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"},
|
||||||
|
]
|
||||||
@@ -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,0 +1,3 @@
|
|||||||
|
Chapter 2, Interactive Python Shells
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
>>> 'Hello world!'
|
||||||
|
'Hello world!'
|
||||||
@@ -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!
|
||||||
@@ -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))
|
||||||
@@ -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
|
||||||
@@ -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,0 +1,4 @@
|
|||||||
|
with open('reload.txt', 'a+') as fh:
|
||||||
|
fh.write('x')
|
||||||
|
fh.seek(0)
|
||||||
|
print(fh.read())
|
||||||
@@ -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()))
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
xxxxx
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
sandwich = dict(spam=2, eggs=1, sausage=1)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Chapter 2, Pythonic Syntax
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
| Common Pitfalls and Style Guide explains what Pythonic code is and how to write code that is Pythonic and adheres to the Python philosophy.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Simple formatting
|
||||||
|
|
||||||
|
>>> name = 'Rick'
|
||||||
|
|
||||||
|
>>> 'Hi %s' % name
|
||||||
|
'Hi Rick'
|
||||||
|
|
||||||
|
>>> 'Hi {}'.format(name)
|
||||||
|
'Hi Rick'
|
||||||
|
|
||||||
|
|
||||||
|
>>> value = 1 / 3
|
||||||
|
|
||||||
|
>>> '%.2f' % value
|
||||||
|
'0.33'
|
||||||
|
|
||||||
|
>>> '{:.2f}'.format(value)
|
||||||
|
'0.33'
|
||||||
|
|
||||||
|
|
||||||
|
>>> name = 'Rick'
|
||||||
|
>>> value = 1 / 3
|
||||||
|
|
||||||
|
>>> 'Hi {0}, value: {1:.3f}. Bye {0}'.format(name, value)
|
||||||
|
'Hi Rick, value: 0.333. Bye Rick'
|
||||||
|
|
||||||
|
|
||||||
|
# Named variables
|
||||||
|
|
||||||
|
>>> name = 'Rick'
|
||||||
|
|
||||||
|
>>> 'Hi %(name)s' % dict(name=name)
|
||||||
|
'Hi Rick'
|
||||||
|
|
||||||
|
>>> 'Hi {name}'.format(name=name)
|
||||||
|
'Hi Rick'
|
||||||
|
|
||||||
|
>>> f'Hi {name}'
|
||||||
|
'Hi Rick'
|
||||||
|
|
||||||
|
>>> 'Hi {name}'.format(**globals())
|
||||||
|
'Hi Rick'
|
||||||
|
|
||||||
|
# Arbitrary expressions
|
||||||
|
|
||||||
|
## Accessing dict items
|
||||||
|
|
||||||
|
>>> username = 'wolph'
|
||||||
|
>>> a = 123
|
||||||
|
>>> b = 456
|
||||||
|
>>> some_dict = dict(a=a, b=b)
|
||||||
|
|
||||||
|
>>> f'''a: {some_dict['a']}'''
|
||||||
|
'a: 123'
|
||||||
|
|
||||||
|
>>> f'''sum: {some_dict['a'] + some_dict['b']}'''
|
||||||
|
'sum: 579'
|
||||||
|
|
||||||
|
## Python expressions, specifically an inline if statement
|
||||||
|
|
||||||
|
>>> f'if statement: {a if a > b else b}'
|
||||||
|
'if statement: 456'
|
||||||
|
|
||||||
|
## Function calls
|
||||||
|
|
||||||
|
>>> f'min: {min(a, b)}'
|
||||||
|
'min: 123'
|
||||||
|
|
||||||
|
>>> f'Hi {username}. And in uppercase: {username.upper()}'
|
||||||
|
'Hi wolph. And in uppercase: WOLPH'
|
||||||
|
|
||||||
|
## Loops
|
||||||
|
|
||||||
|
>>> f'Squares: {[x ** 2 for x in range(5)]}'
|
||||||
|
'Squares: [0, 1, 4, 9, 16]'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
>>> import this
|
||||||
|
The Zen of Python, by Tim Peters
|
||||||
|
<BLANKLINE>
|
||||||
|
Beautiful is better than ugly.
|
||||||
|
Explicit is better than implicit.
|
||||||
|
Simple is better than complex.
|
||||||
|
Complex is better than complicated.
|
||||||
|
Flat is better than nested.
|
||||||
|
Sparse is better than dense.
|
||||||
|
Readability counts.
|
||||||
|
Special cases aren't special enough to break the rules.
|
||||||
|
Although practicality beats purity.
|
||||||
|
Errors should never pass silently.
|
||||||
|
Unless explicitly silenced.
|
||||||
|
In the face of ambiguity, refuse the temptation to guess.
|
||||||
|
There should be one-- and preferably only one --obvious way to do it.
|
||||||
|
Although that way may not be obvious at first unless you're Dutch.
|
||||||
|
Now is better than never.
|
||||||
|
Although never is often better than *right* now.
|
||||||
|
If the implementation is hard to explain, it's a bad idea.
|
||||||
|
If the implementation is easy to explain, it may be a good idea.
|
||||||
|
Namespaces are one honking great idea -- let's do more of those!
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
>>> filter_modulo = lambda i, m: (i[j] for j in \
|
||||||
|
... range(len(i)) if i[j] % m)
|
||||||
|
>>> list(filter_modulo(range(10), 2))
|
||||||
|
[1, 3, 5, 7, 9]
|
||||||
|
|
||||||
|
|
||||||
|
>>> def filter_modulo(items, modulo):
|
||||||
|
... for item in items:
|
||||||
|
... if item % modulo:
|
||||||
|
... yield item
|
||||||
|
...
|
||||||
|
|
||||||
|
>>> list(filter_modulo(range(10), 2))
|
||||||
|
[1, 3, 5, 7, 9]
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
>>> from os import *
|
||||||
|
>>> from asyncio import *
|
||||||
|
|
||||||
|
>>> assert wait
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>>> from os import path
|
||||||
|
>>> from asyncio import wait
|
||||||
|
|
||||||
|
>>> assert wait
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>>> import os
|
||||||
|
>>> import asyncio
|
||||||
|
|
||||||
|
>>> assert asyncio.wait
|
||||||
|
>>> assert os.path
|
||||||
|
|
||||||
|
|
||||||
|
>>> import concurrent.futures
|
||||||
|
|
||||||
|
>>> assert concurrent.futures.wait
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>>> def spam(eggs, *args, **kwargs):
|
||||||
|
... for arg in args:
|
||||||
|
... eggs += arg
|
||||||
|
... for extra_egg in kwargs.get('extra_eggs', []):
|
||||||
|
... eggs += extra_egg
|
||||||
|
... return eggs
|
||||||
|
|
||||||
|
>>> spam(1, 2, 3, extra_eggs=[4, 5])
|
||||||
|
15
|
||||||
|
|
||||||
|
|
||||||
|
>>> def sum_ints(*args):
|
||||||
|
... total = 0
|
||||||
|
... for arg in args:
|
||||||
|
... total += arg
|
||||||
|
... return total
|
||||||
|
|
||||||
|
>>> sum_ints(1, 2, 3, 4, 5)
|
||||||
|
15
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
>>> import math
|
||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> def primes_complicated():
|
||||||
|
... sieved = dict()
|
||||||
|
... i = 2
|
||||||
|
...
|
||||||
|
... while True:
|
||||||
|
... if i not in sieved:
|
||||||
|
... yield i
|
||||||
|
... sieved[i * i] = [i]
|
||||||
|
... else:
|
||||||
|
... for j in sieved[i]:
|
||||||
|
... sieved.setdefault(i + j, []).append(j)
|
||||||
|
... del sieved[i]
|
||||||
|
...
|
||||||
|
... i += 1
|
||||||
|
|
||||||
|
>>> list(itertools.islice(primes_complicated(), 10))
|
||||||
|
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
|
||||||
|
|
||||||
|
|
||||||
|
>>> def primes_complex():
|
||||||
|
... numbers = itertools.count(2)
|
||||||
|
... while True:
|
||||||
|
... yield (prime := next(numbers))
|
||||||
|
... numbers = filter(prime.__rmod__, numbers)
|
||||||
|
|
||||||
|
>>> list(itertools.islice(primes_complex(), 10))
|
||||||
|
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
|
||||||
|
|
||||||
|
>>> def is_prime(number):
|
||||||
|
... if number == 0 or number == 1:
|
||||||
|
... return False
|
||||||
|
... for modulo in range(2, number):
|
||||||
|
... if not number % modulo:
|
||||||
|
... return False
|
||||||
|
... else:
|
||||||
|
... return True
|
||||||
|
|
||||||
|
>>> def primes_simple():
|
||||||
|
... for i in itertools.count():
|
||||||
|
... if is_prime(i):
|
||||||
|
... yield i
|
||||||
|
|
||||||
|
>>> list(itertools.islice(primes_simple(), 10))
|
||||||
|
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
>>> def between_and_modulo(value, a, b, modulo):
|
||||||
|
... if value >= a:
|
||||||
|
... if value <= b:
|
||||||
|
... if value % modulo:
|
||||||
|
... return True
|
||||||
|
... return False
|
||||||
|
|
||||||
|
>>> for i in range(10):
|
||||||
|
... if between_and_modulo(i, 2, 9, 2):
|
||||||
|
... print(i, end=' ')
|
||||||
|
3 5 7 9
|
||||||
|
|
||||||
|
|
||||||
|
>>> def between_and_modulo(value, a, b, modulo):
|
||||||
|
... if value < a:
|
||||||
|
... return False
|
||||||
|
... elif value > b:
|
||||||
|
... return False
|
||||||
|
... elif not value % modulo:
|
||||||
|
... return False
|
||||||
|
... else:
|
||||||
|
... return True
|
||||||
|
|
||||||
|
>>> for i in range(10):
|
||||||
|
... if between_and_modulo(i, 2, 9, 2):
|
||||||
|
... print(i, end=' ')
|
||||||
|
3 5 7 9
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
>>> f=lambda x:0**x or x*f(x-1)
|
||||||
|
>>> f(40)
|
||||||
|
815915283247897734345611269596115894272000000000
|
||||||
|
|
||||||
|
>>> def factorial(x):
|
||||||
|
... if 0 ** x:
|
||||||
|
... return 1
|
||||||
|
... else:
|
||||||
|
... return x * factorial(x - 1)
|
||||||
|
>>> factorial(40)
|
||||||
|
815915283247897734345611269596115894272000000000
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
>>> from functools import reduce
|
||||||
|
|
||||||
|
>>> fib=lambda n:n if n<2 else fib(n-1)+fib(n-2)
|
||||||
|
>>> fib(10)
|
||||||
|
55
|
||||||
|
|
||||||
|
>>> fib=lambda n:reduce(lambda x,y:(x[0]+x[1],x[0]),[(1,1)]*(n-1))[0]
|
||||||
|
>>> fib(10)
|
||||||
|
55
|
||||||
|
|
||||||
|
|
||||||
|
>>> def fib(n):
|
||||||
|
... if n < 2:
|
||||||
|
... return n
|
||||||
|
... else:
|
||||||
|
... return fib(n - 1) + fib(n - 2)
|
||||||
|
|
||||||
|
>>> fib(10)
|
||||||
|
55
|
||||||
|
|
||||||
|
|
||||||
|
>>> def fib(n):
|
||||||
|
... a = 0
|
||||||
|
... b = 1
|
||||||
|
... for _ in range(n):
|
||||||
|
... a, b = b, a + b
|
||||||
|
...
|
||||||
|
... return a
|
||||||
|
|
||||||
|
>>> fib(10)
|
||||||
|
55
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
>>> from concurrent.futures import ProcessPoolExecutor, \
|
||||||
|
... CancelledError, TimeoutError
|
||||||
|
|
||||||
|
>>> from concurrent.futures import (
|
||||||
|
... ProcessPoolExecutor, CancelledError, TimeoutError)
|
||||||
|
|
||||||
|
>>> from concurrent import futures
|
||||||
|
|
||||||
|
>>> from concurrent.futures.process import (
|
||||||
|
... ProcessPoolExecutor
|
||||||
|
... )
|
||||||
|
|
||||||
|
>>> from concurrent.futures import (
|
||||||
|
... ProcessPoolExecutor,
|
||||||
|
... CancelledError,
|
||||||
|
... TimeoutError,
|
||||||
|
... )
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
>>> some_user_input = '123abc'
|
||||||
|
>>> try:
|
||||||
|
... value = int(some_user_input)
|
||||||
|
... except:
|
||||||
|
... pass
|
||||||
|
|
||||||
|
|
||||||
|
>>> some_user_input = '123abc'
|
||||||
|
>>> try:
|
||||||
|
... value = int(some_user_input)
|
||||||
|
... except ValueError:
|
||||||
|
... pass
|
||||||
|
|
||||||
|
|
||||||
|
>>> import logging
|
||||||
|
|
||||||
|
>>> some_user_input = '123abc'
|
||||||
|
>>> try:
|
||||||
|
... value = int(some_user_input)
|
||||||
|
... except Exception as exception:
|
||||||
|
... logging.exception('Uncaught: {exception!r}')
|
||||||
|
|
||||||
|
|
||||||
|
>>> some_user_input_a = '123'
|
||||||
|
>>> some_user_input_b = 'abc'
|
||||||
|
>>> try:
|
||||||
|
... value = int(some_user_input_a)
|
||||||
|
... value += int(some_user_input_b)
|
||||||
|
... except:
|
||||||
|
... value = 0
|
||||||
|
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... 1 / 0 # Raises ZeroDivisionError
|
||||||
|
... except ZeroDivisionError:
|
||||||
|
... print('Got zero division error')
|
||||||
|
... except Exception as exception:
|
||||||
|
... print(f'Got unexpected exception: {exception}')
|
||||||
|
... except BaseException as exception:
|
||||||
|
... # Base exceptions are a special case for keyboard
|
||||||
|
... # interrupts and a few other exceptions that are not
|
||||||
|
... # technically errors.
|
||||||
|
... print(f'Got base exception: {exception}')
|
||||||
|
... else:
|
||||||
|
... print('No exceptions happened, we can continue')
|
||||||
|
... finally:
|
||||||
|
... # Useful cleanup functions such as closing a file
|
||||||
|
... print('This code is _always_ executed')
|
||||||
|
Got zero division error
|
||||||
|
This code is _always_ executed
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
>>> fh_a = open('spam', 'w', -1, None, None, '\n')
|
||||||
|
>>> fh_b = open(file='spam', mode='w', buffering=-1, newline='\n')
|
||||||
|
|
||||||
|
>>> filename = 'spam'
|
||||||
|
>>> mode = 'w'
|
||||||
|
>>> buffers = -1
|
||||||
|
>>> fh_b = open(filename, mode, buffers, newline='\n')
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
>>> import warnings
|
||||||
|
|
||||||
|
>>> warnings.warn('Something deprecated', DeprecationWarning)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
>>> from json import loads
|
||||||
|
|
||||||
|
>>> loads('{}')
|
||||||
|
{}
|
||||||
|
|
||||||
|
>>> import json
|
||||||
|
|
||||||
|
>>> json.loads('{}')
|
||||||
|
{}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
>>> timestamp = 12345
|
||||||
|
>>> filename = f'{timestamp}.csv'
|
||||||
|
|
||||||
|
>>> import datetime
|
||||||
|
|
||||||
|
>>> timestamp = 12345
|
||||||
|
|
||||||
|
>>> if isinstance(timestamp, datetime.datetime):
|
||||||
|
... filename = f'{timestamp}.csv'
|
||||||
|
... else:
|
||||||
|
... raise TypeError(f'{timestamp} is not a valid datetime')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: 12345 is not a valid datetime
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>>> import datetime
|
||||||
|
|
||||||
|
>>> timestamp = datetime.date(2000, 10, 5)
|
||||||
|
>>> filename = f'{timestamp}.csv'
|
||||||
|
>>> print(f'Filename from date: {filename}')
|
||||||
|
Filename from date: 2000-10-05.csv
|
||||||
|
|
||||||
|
>>> timestamp = '2000-10-05'
|
||||||
|
>>> filename = f'{timestamp}.csv'
|
||||||
|
>>> print(f'Filename from str: {filename}')
|
||||||
|
Filename from str: 2000-10-05.csv
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
>>> a = 1
|
||||||
|
>>> a == True
|
||||||
|
True
|
||||||
|
>>> a is True
|
||||||
|
False
|
||||||
|
|
||||||
|
>>> b = 0
|
||||||
|
>>> b == False
|
||||||
|
True
|
||||||
|
>>> b is False
|
||||||
|
False
|
||||||
|
|
||||||
|
>>> def some_unsafe_function(arg=None):
|
||||||
|
... if not arg:
|
||||||
|
... arg = 123
|
||||||
|
...
|
||||||
|
... return arg
|
||||||
|
|
||||||
|
>>> some_unsafe_function(0)
|
||||||
|
123
|
||||||
|
>>> some_unsafe_function(None)
|
||||||
|
123
|
||||||
|
|
||||||
|
|
||||||
|
>>> def some_safe_function(arg=None):
|
||||||
|
... if arg is None:
|
||||||
|
... arg = 123
|
||||||
|
...
|
||||||
|
... return arg
|
||||||
|
|
||||||
|
>>> some_safe_function(0)
|
||||||
|
0
|
||||||
|
>>> some_safe_function(None)
|
||||||
|
123
|
||||||
|
|
||||||
|
|
||||||
|
>>> a = 200 + 56
|
||||||
|
>>> b = 256
|
||||||
|
>>> c = 200 + 57
|
||||||
|
>>> d = 257
|
||||||
|
|
||||||
|
>>> a == b
|
||||||
|
True
|
||||||
|
>>> a is b
|
||||||
|
True
|
||||||
|
>>> c == d
|
||||||
|
True
|
||||||
|
>>> c is d
|
||||||
|
False
|
||||||
|
|
||||||
|
|
||||||
|
>>> spam = list(range(1000000))
|
||||||
|
>>> eggs = list(range(1000000))
|
||||||
|
|
||||||
|
>>> spam == eggs
|
||||||
|
True
|
||||||
|
>>> spam is eggs
|
||||||
|
False
|
||||||
@@ -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)]
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
some_number: int
|
||||||
|
some_number = 'test'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
def spam(a,b,c):print(a,b+c)
|
||||||
|
def eggs():pass
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
>>> some_variable = 123
|
||||||
|
|
||||||
|
>>> match some_variable:
|
||||||
|
... case 1:
|
||||||
|
... print('Got 1')
|
||||||
|
... case 2:
|
||||||
|
... print('Got 2')
|
||||||
|
... case _:
|
||||||
|
... print('Got something else')
|
||||||
|
Got something else
|
||||||
|
|
||||||
|
>>> if some_variable == 1:
|
||||||
|
... print('Got 1')
|
||||||
|
... elif some_variable == 1:
|
||||||
|
... print('Got 2')
|
||||||
|
... else:
|
||||||
|
... print('Got something else')
|
||||||
|
Got something else
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> some_variable = 123
|
||||||
|
|
||||||
|
>>> match some_variable:
|
||||||
|
... case 1:
|
||||||
|
... print('Got 1')
|
||||||
|
... case other:
|
||||||
|
... print('Got something else:', other)
|
||||||
|
Got something else: 123
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> class Direction:
|
||||||
|
... LEFT = -1
|
||||||
|
... RIGHT = 1
|
||||||
|
|
||||||
|
>>> some_variable = Direction.LEFT
|
||||||
|
|
||||||
|
>>> match some_variable:
|
||||||
|
... case Direction.LEFT:
|
||||||
|
... print('Going left')
|
||||||
|
... case Direction.RIGHT:
|
||||||
|
... print('Going right')
|
||||||
|
Going left
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> class Direction:
|
||||||
|
... LEFT = -1
|
||||||
|
... UP = 0
|
||||||
|
... RIGHT = 1
|
||||||
|
... DOWN = 2
|
||||||
|
|
||||||
|
>>> some_variable = Direction.LEFT
|
||||||
|
|
||||||
|
>>> match some_variable:
|
||||||
|
... case Direction.LEFT | Direction.RIGHT:
|
||||||
|
... print('Going horizontal')
|
||||||
|
... case Direction.UP | Direction.DOWN:
|
||||||
|
... print('Going vertical')
|
||||||
|
Going horizontal
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> values = -1, 0, 1
|
||||||
|
>>> for value in values:
|
||||||
|
... print('matching', value, end=': ')
|
||||||
|
... match value:
|
||||||
|
... case negative if negative < 0:
|
||||||
|
... print(f'{negative} is smaller than 0')
|
||||||
|
... case positive if positive > 0:
|
||||||
|
... print(f'{positive} is greater than 0')
|
||||||
|
... case _:
|
||||||
|
... print('no match')
|
||||||
|
matching -1: -1 is smaller than 0
|
||||||
|
matching 0: no match
|
||||||
|
matching 1: 1 is greater than 0
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> values = (0, 1), (0, 2), (1, 2)
|
||||||
|
>>> for value in values:
|
||||||
|
... print('matching', value, end=': ')
|
||||||
|
... match value:
|
||||||
|
... case 0, 1:
|
||||||
|
... print('exactly matched 0, 1')
|
||||||
|
... case 0, y:
|
||||||
|
... print(f'matched 0, y with y: {y}')
|
||||||
|
... case x, y:
|
||||||
|
... print(f'matched x, y with x, y: {x}, {y}')
|
||||||
|
matching (0, 1): exactly matched 0, 1
|
||||||
|
matching (0, 2): matched 0, y with y: 2
|
||||||
|
matching (1, 2): matched x, y with x, y: 1, 2
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> def get_uri(*args):
|
||||||
|
... # Set defaults so we only have to store changed variables
|
||||||
|
... protocol, port, paths = 'https', 443, ()
|
||||||
|
... match args:
|
||||||
|
... case (hostname,):
|
||||||
|
... pass
|
||||||
|
... case (hostname, port):
|
||||||
|
... pass
|
||||||
|
... case (hostname, port, protocol, *paths):
|
||||||
|
... pass
|
||||||
|
... case _:
|
||||||
|
... raise RuntimeError(f'Invalid arguments {args}')
|
||||||
|
...
|
||||||
|
... path = '/'.join(paths)
|
||||||
|
... return f'{protocol}://{hostname}:{port}/{path}'
|
||||||
|
|
||||||
|
>>> get_uri('localhost')
|
||||||
|
'https://localhost:443/'
|
||||||
|
>>> get_uri('localhost', 12345)
|
||||||
|
'https://localhost:12345/'
|
||||||
|
>>> get_uri('localhost', 80, 'http')
|
||||||
|
'http://localhost:80/'
|
||||||
|
>>> get_uri('localhost', 80, 'http', 'some', 'paths')
|
||||||
|
'http://localhost:80/some/paths'
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> values = (0, 1), (0, 2), (1, 2)
|
||||||
|
>>> for value in values:
|
||||||
|
... print('matching', value, end=': ')
|
||||||
|
... match value:
|
||||||
|
... case 0 as x, (1 | 2) as y:
|
||||||
|
... print(f'matched x, y with x, y: {x}, {y}')
|
||||||
|
... case _:
|
||||||
|
... print('no match')
|
||||||
|
matching (0, 1): matched x, y with x, y: 0, 1
|
||||||
|
matching (0, 2): matched x, y with x, y: 0, 2
|
||||||
|
matching (1, 2): no match
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> values = dict(a=0, b=0), dict(a=0, b=1), dict(a=1, b=1)
|
||||||
|
>>> for value in values:
|
||||||
|
... print('matching', value, end=': ')
|
||||||
|
... match value:
|
||||||
|
... case {'a': 0}:
|
||||||
|
... print('matched a=0:', value)
|
||||||
|
... case {'a': 0, 'b': 0}:
|
||||||
|
... print('matched a=0, b=0:', value)
|
||||||
|
... case _:
|
||||||
|
... print('no match')
|
||||||
|
matching {'a': 0, 'b': 0}: matched a=0: {'a': 0, 'b': 0}
|
||||||
|
matching {'a': 0, 'b': 1}: matched a=0: {'a': 0, 'b': 1}
|
||||||
|
matching {'a': 1, 'b': 1}: no match
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> class Person:
|
||||||
|
... def __init__(self, name):
|
||||||
|
... self.name = name
|
||||||
|
|
||||||
|
>>> values = Person('Rick'), Person('Guido')
|
||||||
|
>>> for value in values:
|
||||||
|
... match value:
|
||||||
|
... case Person(name='Rick'):
|
||||||
|
... print('I found Rick')
|
||||||
|
... case Person(occupation='Programmer'):
|
||||||
|
... print('I found a programmer')
|
||||||
|
... case Person() as person:
|
||||||
|
... print('I found a person:', person.name)
|
||||||
|
I found Rick
|
||||||
|
I found a person: Guido
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
>>> class Person:
|
||||||
|
... def __init__(self, name):
|
||||||
|
... self.name = name
|
||||||
|
|
||||||
|
>>> value = Person(123)
|
||||||
|
>>> match value:
|
||||||
|
... case Person(name=str() as name):
|
||||||
|
... print('Found person with str name:', name)
|
||||||
|
... case Person(name=int() as name):
|
||||||
|
... print('Found person with int name:', name)
|
||||||
|
Found person with int name: 123
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
>>> g = 1
|
||||||
|
>>> def print_global():
|
||||||
|
... print(f'Value: {g}')
|
||||||
|
|
||||||
|
>>> print_global()
|
||||||
|
Value: 1
|
||||||
|
|
||||||
|
>>> g = 1
|
||||||
|
|
||||||
|
>>> def print_global():
|
||||||
|
... g += 1
|
||||||
|
... print(f'Value: {g}')
|
||||||
|
|
||||||
|
>>> print_global()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
UnboundLocalError: local variable 'g' referenced before assignment
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
>>> import copy
|
||||||
|
|
||||||
|
>>> x = [[1], [2, 3]]
|
||||||
|
>>> y = x.copy()
|
||||||
|
>>> z = copy.deepcopy(x)
|
||||||
|
|
||||||
|
>>> x.append('a')
|
||||||
|
>>> x[0].append(x)
|
||||||
|
|
||||||
|
>>> x
|
||||||
|
[[1, [...]], [2, 3], 'a']
|
||||||
|
>>> y
|
||||||
|
[[1, [...]], [2, 3]]
|
||||||
|
>>> z
|
||||||
|
[[1], [2, 3]]
|
||||||
|
|
||||||
|
|
||||||
|
>>> def append(list_=[], value='value'):
|
||||||
|
... list_.append(value)
|
||||||
|
... return list_
|
||||||
|
|
||||||
|
>>> append(value='a')
|
||||||
|
['a']
|
||||||
|
>>> append(value='b')
|
||||||
|
['a', 'b']
|
||||||
|
|
||||||
|
|
||||||
|
>>> def append(list_=None, value='value'):
|
||||||
|
... if list_ is None:
|
||||||
|
... list_ = []
|
||||||
|
... list_.append(value)
|
||||||
|
... return list_
|
||||||
|
|
||||||
|
>>> append(value='a')
|
||||||
|
['a']
|
||||||
|
>>> append(value='b')
|
||||||
|
['b']
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
>>> class SomeClass:
|
||||||
|
... class_list = []
|
||||||
|
...
|
||||||
|
... def __init__(self):
|
||||||
|
... self.instance_list = []
|
||||||
|
|
||||||
|
>>> SomeClass.class_list.append('from class')
|
||||||
|
>>> instance = SomeClass()
|
||||||
|
>>> instance.class_list.append('from instance')
|
||||||
|
>>> instance.instance_list.append('from instance')
|
||||||
|
|
||||||
|
>>> SomeClass.class_list
|
||||||
|
['from class', 'from instance']
|
||||||
|
>>> SomeClass.instance_list
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: ... 'SomeClass' has no attribute 'instance_list'
|
||||||
|
|
||||||
|
>>> instance.class_list
|
||||||
|
['from class', 'from instance']
|
||||||
|
>>> instance.instance_list
|
||||||
|
['from instance']
|
||||||
|
|
||||||
|
|
||||||
|
>>> class Parent:
|
||||||
|
... pass
|
||||||
|
|
||||||
|
|
||||||
|
>>> class Child(Parent):
|
||||||
|
... pass
|
||||||
|
|
||||||
|
|
||||||
|
>>> Parent.parent_property = 'parent'
|
||||||
|
>>> Child.parent_property
|
||||||
|
'parent'
|
||||||
|
|
||||||
|
>>> Child.parent_property = 'child'
|
||||||
|
>>> Parent.parent_property
|
||||||
|
'parent'
|
||||||
|
>>> Child.parent_property
|
||||||
|
'child'
|
||||||
|
|
||||||
|
>>> Child.child_property = 'child'
|
||||||
|
>>> Parent.child_property
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: ... 'Parent' has no attribute 'child_property'
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import builtins
|
||||||
|
import inspect
|
||||||
|
import pprint
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def pp(*args, **kwargs):
|
||||||
|
'''PrettyPrint function that prints the variable name when
|
||||||
|
available and pprints the data
|
||||||
|
|
||||||
|
>>> x = 10
|
||||||
|
>>> pp(x)
|
||||||
|
# x: 10
|
||||||
|
'''
|
||||||
|
# Fetch the current frame from the stack
|
||||||
|
frame = inspect.currentframe().f_back
|
||||||
|
# Prepare the frame info
|
||||||
|
frame_info = inspect.getframeinfo(frame)
|
||||||
|
|
||||||
|
# Walk through the lines of the function
|
||||||
|
for line in frame_info[3]:
|
||||||
|
# Search for the pp() function call with a fancy regexp
|
||||||
|
m = re.search(r'\bpp\s*\(\s*([^)]*)\s*\)', line)
|
||||||
|
if m:
|
||||||
|
print('# %s:' % m.group(1), end=' ')
|
||||||
|
break
|
||||||
|
|
||||||
|
pprint.pprint(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
builtins.pf = pprint.pformat
|
||||||
|
builtins.pp = pp
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
>>> list = list((1, 2, 3))
|
||||||
|
>>> list
|
||||||
|
[1, 2, 3]
|
||||||
|
|
||||||
|
>>> list((4, 5, 6))
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: 'list' object is not callable
|
||||||
|
|
||||||
|
>>> import = 'Some import'
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SyntaxError: invalid syntax
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
>>> dict_ = dict(a=123)
|
||||||
|
>>> set_ = set((456,))
|
||||||
|
|
||||||
|
>>> for key in dict_:
|
||||||
|
... del dict_[key]
|
||||||
|
...
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<stdin>", line 1, in <module>
|
||||||
|
RuntimeError: dictionary changed size during iteration
|
||||||
|
|
||||||
|
>>> for item in set_:
|
||||||
|
... set_.remove(item)
|
||||||
|
...
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<stdin>", line 1, in <module>
|
||||||
|
RuntimeError: Set changed size during iteration
|
||||||
|
|
||||||
|
|
||||||
|
>>> list_ = list(range(10))
|
||||||
|
>>> list_
|
||||||
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
|
||||||
|
>>> for item in list_:
|
||||||
|
... print(list_.pop(0), end=', ')
|
||||||
|
0, 1, 2, 3, 4,
|
||||||
|
|
||||||
|
>>> list_
|
||||||
|
[5, 6, 7, 8, 9]
|
||||||
|
|
||||||
|
|
||||||
|
>>> list_ = list(range(10))
|
||||||
|
|
||||||
|
>>> for item in list(list_):
|
||||||
|
... print(list_.pop(0), end=', ')
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
>>> exception = None
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... 1 / 0
|
||||||
|
... except ZeroDivisionError as exception:
|
||||||
|
... pass
|
||||||
|
|
||||||
|
>>> exception
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
NameError: name 'exception' is not defined
|
||||||
|
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... 1 / 0
|
||||||
|
... except ZeroDivisionError as exception:
|
||||||
|
... new_exception = exception
|
||||||
|
|
||||||
|
>>> new_exception
|
||||||
|
ZeroDivisionError('division by zero')
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
>>> functions = [lambda: i for i in range(3)]
|
||||||
|
|
||||||
|
>>> for function in functions:
|
||||||
|
... print(function(), end=', ')
|
||||||
|
2, 2, 2,
|
||||||
|
|
||||||
|
|
||||||
|
>>> from functools import partial
|
||||||
|
|
||||||
|
>>> functions = [partial(lambda x: x, i) for i in range(3)]
|
||||||
|
|
||||||
|
>>> for function in functions:
|
||||||
|
... print(function(), end=', ')
|
||||||
|
0, 1, 2,
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import T_28_circular_imports_b
|
||||||
|
|
||||||
|
|
||||||
|
class FileA:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FileC(T_28_circular_imports_b.FileB):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import T_28_circular_imports_a
|
||||||
|
|
||||||
|
|
||||||
|
class FileB(T_28_circular_imports_a.FileA):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
class FileA:
|
||||||
|
pass
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import T_29_circular_imports_a
|
||||||
|
|
||||||
|
|
||||||
|
class FileB(T_29_circular_imports_a.FileA):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import T_29_circular_imports_b
|
||||||
|
|
||||||
|
|
||||||
|
class FileC(T_29_circular_imports_b.FileB):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
>>> import importlib
|
||||||
|
|
||||||
|
>>> module_name = 'sys'
|
||||||
|
>>> attribute = 'version_info'
|
||||||
|
|
||||||
|
>>> module = importlib.import_module(module_name)
|
||||||
|
>>> module
|
||||||
|
<module 'sys' (built-in)>
|
||||||
|
>>> getattr(module, attribute).major
|
||||||
|
3
|
||||||
@@ -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()))
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Chapter 3, Containers and Collections
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
| Storing Data the Right Way using the many containers and collections bundled with Python to create code that is fast and readable.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
>>> n = 1000
|
||||||
|
>>> a = list(range(n))
|
||||||
|
>>> b = dict.fromkeys(range(n))
|
||||||
|
>>> for i in range(100):
|
||||||
|
... assert i in a # takes n=1000 steps
|
||||||
|
... assert i in b # takes 1 step
|
||||||
|
|
||||||
|
|
||||||
|
>>> def o_one(items):
|
||||||
|
... return 1 # 1 operation so O(1)
|
||||||
|
|
||||||
|
>>> def o_n(items):
|
||||||
|
... total = 0
|
||||||
|
... # Walks through all items once so O(n)
|
||||||
|
... for item in items:
|
||||||
|
... total += item
|
||||||
|
... return total
|
||||||
|
|
||||||
|
>>> def o_n_squared(items):
|
||||||
|
... total = 0
|
||||||
|
... # Walks through all items n*n times so O(n**2)
|
||||||
|
... for a in items:
|
||||||
|
... for b in items:
|
||||||
|
... total += a * b
|
||||||
|
... return total
|
||||||
|
|
||||||
|
>>> n = 10
|
||||||
|
>>> items = range(n)
|
||||||
|
>>> o_one(items) # 1 operation
|
||||||
|
1
|
||||||
|
>>> o_n(items) # n = 10 operations
|
||||||
|
45
|
||||||
|
>>> o_n_squared(items) # n*n = 10*10 = 100 operations
|
||||||
|
2025
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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']
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
>>> spam = 1, 2, 3
|
||||||
|
>>> eggs = 4, 5, 6
|
||||||
|
|
||||||
|
>>> data = dict()
|
||||||
|
>>> data[spam] = 'spam'
|
||||||
|
>>> data[eggs] = 'eggs'
|
||||||
|
|
||||||
|
>>> import pprint # Using pprint for consistent and sorted output
|
||||||
|
|
||||||
|
>>> pprint.pprint(data)
|
||||||
|
{(1, 2, 3): 'spam', (4, 5, 6): 'eggs'}
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> spam = 1, 'abc', (2, 3, (4, 5)), 'def'
|
||||||
|
>>> eggs = 4, (spam, 5), 6
|
||||||
|
|
||||||
|
>>> data = dict()
|
||||||
|
>>> data[spam] = 'spam'
|
||||||
|
>>> data[eggs] = 'eggs'
|
||||||
|
>>> import pprint # Using pprint for consistent and sorted output
|
||||||
|
|
||||||
|
>>> pprint.pprint(data)
|
||||||
|
{(1, 'abc', (2, 3, (4, 5)), 'def'): 'spam',
|
||||||
|
(4, ((1, 'abc', (2, 3, (4, 5)), 'def'), 5), 6): 'eggs'}
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Assign using tuples on both sides
|
||||||
|
|
||||||
|
>>> a, b, c = 1, 2, 3
|
||||||
|
>>> a
|
||||||
|
1
|
||||||
|
|
||||||
|
# Assign a tuple to a single variable
|
||||||
|
|
||||||
|
>>> spam = a, (b, c)
|
||||||
|
>>> spam
|
||||||
|
(1, (2, 3))
|
||||||
|
|
||||||
|
# Unpack a tuple to two variables
|
||||||
|
|
||||||
|
>>> a, b = spam
|
||||||
|
>>> a
|
||||||
|
1
|
||||||
|
>>> b
|
||||||
|
(2, 3)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Unpack with variable length objects which assigns a list instead
|
||||||
|
of a tuple
|
||||||
|
|
||||||
|
>>> spam, *eggs = 1, 2, 3, 4
|
||||||
|
>>> spam
|
||||||
|
1
|
||||||
|
>>> eggs
|
||||||
|
[2, 3, 4]
|
||||||
|
|
||||||
|
# Which can be unpacked as well of course
|
||||||
|
|
||||||
|
>>> a, b, c = eggs
|
||||||
|
>>> c
|
||||||
|
4
|
||||||
|
|
||||||
|
# This works for ranges as well
|
||||||
|
|
||||||
|
>>> spam, *eggs = range(10)
|
||||||
|
>>> spam
|
||||||
|
0
|
||||||
|
>>> eggs
|
||||||
|
[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
|
||||||
|
# And it works both ways
|
||||||
|
|
||||||
|
>>> a, b, *c = a, *eggs
|
||||||
|
>>> a, b
|
||||||
|
(2, 1)
|
||||||
|
>>> c
|
||||||
|
[2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> def eggs(*args):
|
||||||
|
... print('args:', args)
|
||||||
|
|
||||||
|
>>> eggs(1, 2, 3)
|
||||||
|
args: (1, 2, 3)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> def spam_eggs():
|
||||||
|
... return 'spam', 'eggs'
|
||||||
|
|
||||||
|
>>> spam, eggs = spam_eggs()
|
||||||
|
>>> spam
|
||||||
|
'spam'
|
||||||
|
>>> eggs
|
||||||
|
'eggs'
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
>>> spam: int
|
||||||
|
>>> __annotations__['spam']
|
||||||
|
<class 'int'>
|
||||||
|
>>> spam = 'not a number'
|
||||||
|
>>> __annotations__['spam']
|
||||||
|
<class 'int'>
|
||||||
|
|
||||||
|
|
||||||
|
>>> import dataclasses
|
||||||
|
|
||||||
|
>>> @dataclasses.dataclass
|
||||||
|
... class Sandwich:
|
||||||
|
... spam: int
|
||||||
|
... eggs: int = 3
|
||||||
|
|
||||||
|
>>> Sandwich(1, 2)
|
||||||
|
Sandwich(spam=1, eggs=2)
|
||||||
|
|
||||||
|
>>> sandwich = Sandwich(4)
|
||||||
|
>>> sandwich
|
||||||
|
Sandwich(spam=4, eggs=3)
|
||||||
|
>>> sandwich.eggs
|
||||||
|
3
|
||||||
|
>>> dataclasses.asdict(sandwich)
|
||||||
|
{'spam': 4, 'eggs': 3}
|
||||||
|
>>> dataclasses.astuple(sandwich)
|
||||||
|
(4, 3)
|
||||||
|
|
||||||
|
>>> help(dataclasses.dataclass)
|
||||||
|
Help on ... dataclass(..., *, init=True, repr=True, eq=True, ...
|
||||||
|
|
||||||
|
>>> def __init__(self, spam, eggs=3):
|
||||||
|
... self.spam = spam
|
||||||
|
... self.eggs = eggs
|
||||||
|
|
||||||
|
|
||||||
|
>>> import typing
|
||||||
|
|
||||||
|
>>> @dataclasses.dataclass
|
||||||
|
... class Group:
|
||||||
|
... name: str
|
||||||
|
... parent: 'Group' = None
|
||||||
|
|
||||||
|
>>> @dataclasses.dataclass
|
||||||
|
... class User:
|
||||||
|
... username: str
|
||||||
|
... email: str = None
|
||||||
|
... groups: typing.List[Group] = None
|
||||||
|
|
||||||
|
>>> users = Group('users')
|
||||||
|
>>> admins = Group('admins', users)
|
||||||
|
>>> rick = User('rick', groups=[admins])
|
||||||
|
>>> gvr = User('gvanrossum', 'guido@python.org', [admins])
|
||||||
|
|
||||||
|
>>> rick.groups
|
||||||
|
[Group(name='admins', parent=Group(name='users', parent=None))]
|
||||||
|
|
||||||
|
>>> rick.groups[0].parent
|
||||||
|
Group(name='users', parent=None)
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
>>> import builtins
|
||||||
|
|
||||||
|
>>> builtin_vars = vars(builtins)
|
||||||
|
|
||||||
|
>>> key = 'something to search for'
|
||||||
|
|
||||||
|
>>> if key in locals():
|
||||||
|
... value = locals()[key]
|
||||||
|
... elif key in globals():
|
||||||
|
... value = globals()[key]
|
||||||
|
... elif key in builtin_vars:
|
||||||
|
... value = builtin_vars[key]
|
||||||
|
... else:
|
||||||
|
... raise NameError(f'name {key!r} is not defined')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
NameError: name 'something to search for' is not defined
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
>>> mappings = locals(), globals(), vars(builtins)
|
||||||
|
>>> for mapping in mappings:
|
||||||
|
... if key in mapping:
|
||||||
|
... value = mapping[key]
|
||||||
|
... break
|
||||||
|
... else:
|
||||||
|
... raise NameError(f'name {key!r} is not defined')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
NameError: name 'something to search for' is not defined
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
>>> mappings = collections.ChainMap(
|
||||||
|
... locals(), globals(), vars(builtins))
|
||||||
|
>>> mappings[key]
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
KeyError: 'something to search for'
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
>>> import json
|
||||||
|
>>> import pathlib
|
||||||
|
>>> import argparse
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
>>> DEFAULT = dict(verbosity=1)
|
||||||
|
|
||||||
|
>>> config_file = pathlib.Path('config.json')
|
||||||
|
>>> if config_file.exists():
|
||||||
|
... config = json.load(config_file.open())
|
||||||
|
... else:
|
||||||
|
... config = dict()
|
||||||
|
|
||||||
|
>>> parser = argparse.ArgumentParser()
|
||||||
|
>>> parser.add_argument('-v', '--verbose', action='count',
|
||||||
|
... dest='verbosity')
|
||||||
|
_CountAction(...)
|
||||||
|
|
||||||
|
>>> args, _ = parser.parse_known_args(args=['-v'])
|
||||||
|
>>> defined_args = {k: v for k, v in vars(args).items() if v}
|
||||||
|
>>> combined = collections.ChainMap(defined_args, config, DEFAULT)
|
||||||
|
>>> combined['verbosity']
|
||||||
|
1
|
||||||
|
|
||||||
|
>>> args, _ = parser.parse_known_args(['-vv'])
|
||||||
|
>>> defined_args = {k: v for k, v in vars(args).items() if v}
|
||||||
|
>>> combined = collections.ChainMap(defined_args, config, DEFAULT)
|
||||||
|
>>> combined['verbosity']
|
||||||
|
2
|
||||||
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
>>> nodes = [
|
||||||
|
... ('a', 'b'),
|
||||||
|
... ('a', 'c'),
|
||||||
|
... ('b', 'a'),
|
||||||
|
... ('b', 'd'),
|
||||||
|
... ('c', 'a'),
|
||||||
|
... ('d', 'a'),
|
||||||
|
... ('d', 'b'),
|
||||||
|
... ('d', 'c'),
|
||||||
|
... ]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> graph = dict()
|
||||||
|
>>> for from_, to in nodes:
|
||||||
|
... if from_ not in graph:
|
||||||
|
... graph[from_] = []
|
||||||
|
... graph[from_].append(to)
|
||||||
|
|
||||||
|
>>> import pprint
|
||||||
|
|
||||||
|
>>> pprint.pprint(graph)
|
||||||
|
{'a': ['b', 'c'],
|
||||||
|
'b': ['a', 'd'],
|
||||||
|
'c': ['a'],
|
||||||
|
'd': ['a', 'b', 'c']}
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
>>> graph = collections.defaultdict(list)
|
||||||
|
>>> for from_, to in nodes:
|
||||||
|
... graph[from_].append(to)
|
||||||
|
|
||||||
|
>>> import pprint
|
||||||
|
|
||||||
|
>>> pprint.pprint(graph)
|
||||||
|
defaultdict(<class 'list'>,
|
||||||
|
{'a': ['b', 'c'],
|
||||||
|
'b': ['a', 'd'],
|
||||||
|
'c': ['a'],
|
||||||
|
'd': ['a', 'b', 'c']})
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> counter = collections.defaultdict(int)
|
||||||
|
>>> counter['spam'] += 5
|
||||||
|
>>> counter
|
||||||
|
defaultdict(<class 'int'>, {'spam': 5})
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
>>> def tree(): return collections.defaultdict(tree)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import json
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
|
||||||
|
>>> def tree():
|
||||||
|
... return collections.defaultdict(tree)
|
||||||
|
|
||||||
|
>>> colours = tree()
|
||||||
|
>>> colours['other']['black'] = 0x000000
|
||||||
|
>>> colours['other']['white'] = 0xFFFFFF
|
||||||
|
>>> colours['primary']['red'] = 0xFF0000
|
||||||
|
>>> colours['primary']['green'] = 0x00FF00
|
||||||
|
>>> colours['primary']['blue'] = 0x0000FF
|
||||||
|
>>> colours['secondary']['yellow'] = 0xFFFF00
|
||||||
|
>>> colours['secondary']['aqua'] = 0x00FFFF
|
||||||
|
>>> colours['secondary']['fuchsia'] = 0xFF00FF
|
||||||
|
|
||||||
|
>>> print(json.dumps(colours, sort_keys=True, indent=4))
|
||||||
|
{
|
||||||
|
"other": {
|
||||||
|
"black": 0,
|
||||||
|
"white": 16777215
|
||||||
|
},
|
||||||
|
"primary": {
|
||||||
|
"blue": 255,
|
||||||
|
"green": 65280,
|
||||||
|
"red": 16711680
|
||||||
|
},
|
||||||
|
"secondary": {
|
||||||
|
"aqua": 65535,
|
||||||
|
"fuchsia": 16711935,
|
||||||
|
"yellow": 16776960
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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]
|
||||||
|
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
>>> import bisect
|
||||||
|
|
||||||
|
Using the regular sort:
|
||||||
|
>>> sorted_list = []
|
||||||
|
>>> sorted_list.append(5) # O(1)
|
||||||
|
|
||||||
|
>>> sorted_list.append(3) # O(1)
|
||||||
|
|
||||||
|
>>> sorted_list.append(1) # O(1)
|
||||||
|
|
||||||
|
>>> sorted_list.append(2) # O(1)
|
||||||
|
|
||||||
|
>>> sorted_list.sort() # O(n * log(n)) = 4 * log(4) = 8
|
||||||
|
|
||||||
|
>>> sorted_list
|
||||||
|
[1, 2, 3, 5]
|
||||||
|
|
||||||
|
Using bisect:
|
||||||
|
>>> sorted_list = []
|
||||||
|
>>> bisect.insort(sorted_list, 5) # O(n) = 1
|
||||||
|
|
||||||
|
>>> bisect.insort(sorted_list, 3) # O(n) = 2
|
||||||
|
|
||||||
|
>>> bisect.insort(sorted_list, 1) # O(n) = 3
|
||||||
|
|
||||||
|
>>> bisect.insort(sorted_list, 2) # O(n) = 4
|
||||||
|
|
||||||
|
>>> sorted_list
|
||||||
|
[1, 2, 3, 5]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> sorted_list = [1, 2, 5]
|
||||||
|
>>> def contains(sorted_list, value):
|
||||||
|
... for item in sorted_list:
|
||||||
|
... if item > value:
|
||||||
|
... break
|
||||||
|
... elif item == value:
|
||||||
|
... return True
|
||||||
|
... return False
|
||||||
|
|
||||||
|
>>> contains(sorted_list, 2) # Need to walk through 2 items, O(n) = 2
|
||||||
|
True
|
||||||
|
>>> contains(sorted_list, 4) # Need to walk through n items, O(n) = 3
|
||||||
|
False
|
||||||
|
>>> contains(sorted_list, 6) # Need to walk through n items, O(n) = 3
|
||||||
|
False
|
||||||
|
|
||||||
|
|
||||||
|
>>> import bisect
|
||||||
|
|
||||||
|
>>> sorted_list = [1, 2, 5]
|
||||||
|
>>> def contains(sorted_list, value):
|
||||||
|
... i = bisect.bisect_left(sorted_list, value)
|
||||||
|
... return i < len(sorted_list) and sorted_list[i] == value
|
||||||
|
|
||||||
|
>>> contains(sorted_list, 2) # Found it the first step, O(log(n)) = 1
|
||||||
|
True
|
||||||
|
>>> contains(sorted_list, 4) # No result after 2 steps, O(log(n)) = 2
|
||||||
|
False
|
||||||
|
>>> contains(sorted_list, 6) # No result after 2 steps, O(log(n)) = 2
|
||||||
|
False
|
||||||
|
|
||||||
|
|
||||||
|
>>> import bisect
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
>>> class SortedList:
|
||||||
|
... def __init__(self, *values):
|
||||||
|
... self._list = sorted(values)
|
||||||
|
...
|
||||||
|
... def index(self, value):
|
||||||
|
... i = bisect.bisect_left(self._list, value)
|
||||||
|
... if i < len(self._list) and self._list[i] == value:
|
||||||
|
... return index
|
||||||
|
...
|
||||||
|
... def delete(self, value):
|
||||||
|
... del self._list[self.index(value)]
|
||||||
|
...
|
||||||
|
... def add(self, value):
|
||||||
|
... bisect.insort(self._list, value)
|
||||||
|
...
|
||||||
|
... def __iter__(self):
|
||||||
|
... for value in self._list:
|
||||||
|
... yield value
|
||||||
|
...
|
||||||
|
... def __exists__(self, value):
|
||||||
|
... return self.index(value) is not None
|
||||||
|
|
||||||
|
>>> sorted_list = SortedList(1, 3, 6, 2)
|
||||||
|
>>> 3 in sorted_list
|
||||||
|
True
|
||||||
|
>>> 5 in sorted_list
|
||||||
|
False
|
||||||
|
>>> sorted_list.add(5)
|
||||||
|
>>> 5 in sorted_list
|
||||||
|
True
|
||||||
|
>>> list(sorted_list)
|
||||||
|
[1, 2, 3, 5, 6]
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
>>> class Borg:
|
||||||
|
... _state = {}
|
||||||
|
... def __init__(self):
|
||||||
|
... self.__dict__ = self._state
|
||||||
|
|
||||||
|
>>> class SubBorg(Borg):
|
||||||
|
... pass
|
||||||
|
|
||||||
|
>>> a = Borg()
|
||||||
|
>>> b = Borg()
|
||||||
|
>>> c = Borg()
|
||||||
|
>>> a.a_property = 123
|
||||||
|
>>> b.a_property
|
||||||
|
123
|
||||||
|
>>> c.a_property
|
||||||
|
123
|
||||||
|
|
||||||
|
|
||||||
|
>>> class Singleton:
|
||||||
|
... def __new__(cls):
|
||||||
|
... if not hasattr(cls, '_instance'):
|
||||||
|
... cls._instance = super(Singleton, cls).__new__(cls)
|
||||||
|
...
|
||||||
|
... return cls._instance
|
||||||
|
|
||||||
|
>>> class SubSingleton(Singleton):
|
||||||
|
... pass
|
||||||
|
|
||||||
|
|
||||||
|
>>> a = Singleton()
|
||||||
|
>>> b = Singleton()
|
||||||
|
>>> c = SubSingleton()
|
||||||
|
>>> a.a_property = 123
|
||||||
|
>>> b.a_property
|
||||||
|
123
|
||||||
|
>>> c.a_property
|
||||||
|
123
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
>>> class Sandwich:
|
||||||
|
...
|
||||||
|
... def __init__(self, spam):
|
||||||
|
... self.spam = spam
|
||||||
|
...
|
||||||
|
... @property
|
||||||
|
... def spam(self):
|
||||||
|
... return self._spam
|
||||||
|
...
|
||||||
|
... @spam.setter
|
||||||
|
... def spam(self, value):
|
||||||
|
... self._spam = value
|
||||||
|
... if self._spam >= 5:
|
||||||
|
... print('You must be hungry')
|
||||||
|
...
|
||||||
|
... @spam.deleter
|
||||||
|
... def spam(self):
|
||||||
|
... self._spam = 0
|
||||||
|
|
||||||
|
>>> sandwich = Sandwich(2)
|
||||||
|
>>> sandwich.spam += 1
|
||||||
|
>>> sandwich.spam += 2
|
||||||
|
You must be hungry
|
||||||
@@ -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,0 +1,4 @@
|
|||||||
|
Chapter 4, Functional Programming
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
| Readability versus Brevity covers the functional programming techniques such as list/dict/set comprehensions and lambda statements that are available in Python. Additionally, it illustrates the similarities to the mathematical principles involved.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
>>> def add_value_functional(items, value):
|
||||||
|
... return items + [value]
|
||||||
|
|
||||||
|
>>> items = [1, 2, 3]
|
||||||
|
>>> add_value_functional(items, 5)
|
||||||
|
[1, 2, 3, 5]
|
||||||
|
>>> items
|
||||||
|
[1, 2, 3]
|
||||||
|
|
||||||
|
>>> def add_value_regular(items, value):
|
||||||
|
... items.append(value)
|
||||||
|
... return items
|
||||||
|
|
||||||
|
>>> add_value_regular(items, 5)
|
||||||
|
[1, 2, 3, 5]
|
||||||
|
>>> items
|
||||||
|
[1, 2, 3, 5]
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
>>> squares = [x ** 2 for x in range(10)]
|
||||||
|
>>> squares
|
||||||
|
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> odd_squares = [x ** 2 for x in range(10) if x % 2]
|
||||||
|
>>> odd_squares
|
||||||
|
[1, 9, 25, 49, 81]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> def square(x):
|
||||||
|
... return x ** 2
|
||||||
|
|
||||||
|
>>> def odd(x):
|
||||||
|
... return x % 2
|
||||||
|
|
||||||
|
>>> squares = list(map(square, range(10)))
|
||||||
|
>>> squares
|
||||||
|
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||||
|
|
||||||
|
>>> odd_squares = list(filter(odd, map(square, range(10))))
|
||||||
|
>>> odd_squares
|
||||||
|
[1, 9, 25, 49, 81]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import os
|
||||||
|
|
||||||
|
>>> directories = filter(os.path.isdir, os.listdir('.'))
|
||||||
|
|
||||||
|
# Versus:
|
||||||
|
|
||||||
|
>>> directories = [x for x in os.listdir('.') if os.path.isdir(x)]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> odd_squares = []
|
||||||
|
>>> for x in range(10):
|
||||||
|
... if x % 2:
|
||||||
|
... odd_squares.append(x ** 2)
|
||||||
|
|
||||||
|
>>> odd_squares
|
||||||
|
[1, 9, 25, 49, 81]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# List comprehension
|
||||||
|
|
||||||
|
>>> [x // 2 for x in range(3)]
|
||||||
|
[0, 0, 1]
|
||||||
|
|
||||||
|
# Set comprehension
|
||||||
|
|
||||||
|
>>> numbers = {x // 2 for x in range(3)}
|
||||||
|
>>> sorted(numbers)
|
||||||
|
[0, 1]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import random
|
||||||
|
|
||||||
|
>>> [random.random() for _ in range(10) if random.random() >= 0.5]
|
||||||
|
... # doctest: +SKIP
|
||||||
|
[0.5211948104577864, 0.650010512129705, 0.021427316545174158]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import random
|
||||||
|
|
||||||
|
>>> numbers = [random.random() for _ in range(10)] # doctest: +SKIP
|
||||||
|
|
||||||
|
>>> [x for x in numbers if x >= 0.5] # doctest: +SKIP
|
||||||
|
[0.715510247827078, 0.8426277505519564, 0.5071133900377911]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import random
|
||||||
|
|
||||||
|
>>> [x for x in [random.random() for _ in range(10)] if x >= 0.5]
|
||||||
|
... # doctest: +SKIP
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import random
|
||||||
|
|
||||||
|
>>> [x for _ in range(10) for x in [random.random()] if x >= 0.5]
|
||||||
|
... # doctest: +SKIP
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> [(x, y) for x in range(3) for y in range(3, 5)]
|
||||||
|
[(0, 3), (0, 4), (1, 3), (1, 4), (2, 3), (2, 4)]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> results = []
|
||||||
|
>>> for x in range(3):
|
||||||
|
... for y in range(3, 5):
|
||||||
|
... results.append((x, y))
|
||||||
|
...
|
||||||
|
>>> results
|
||||||
|
[(0, 3), (0, 4), (1, 3), (1, 4), (2, 3), (2, 4)]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> matrix = [
|
||||||
|
... [1, 2, 3, 4],
|
||||||
|
... [5, 6, 7, 8],
|
||||||
|
... [9, 10, 11, 12],
|
||||||
|
... ]
|
||||||
|
|
||||||
|
>>> reshaped_matrix = [
|
||||||
|
... [
|
||||||
|
... [y for x in matrix for y in x][i * len(matrix) + j]
|
||||||
|
... for j in range(len(matrix))
|
||||||
|
... ]
|
||||||
|
... for i in range(len(matrix[0]))
|
||||||
|
... ]
|
||||||
|
|
||||||
|
>>> import pprint
|
||||||
|
|
||||||
|
>>> pprint.pprint(reshaped_matrix, width=40)
|
||||||
|
[[1, 2, 3],
|
||||||
|
[4, 5, 6],
|
||||||
|
[7, 8, 9],
|
||||||
|
[10, 11, 12]]
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
>>> {x: x ** 2 for x in range(6)}
|
||||||
|
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
|
||||||
|
|
||||||
|
>>> {x: x ** 2 for x in range(6) if x % 2}
|
||||||
|
{1: 1, 3: 9, 5: 25}
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> {x ** 2: [y for y in range(x)] for x in range(5)}
|
||||||
|
{0: [], 1: [0], 4: [0, 1], 9: [0, 1, 2], 16: [0, 1, 2, 3]}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
>>> [x*y for x in range(3) for y in range(3)]
|
||||||
|
[0, 0, 0, 0, 1, 2, 0, 2, 4]
|
||||||
|
|
||||||
|
>>> {x*y for x in range(3) for y in range(3)}
|
||||||
|
{0, 1, 2, 4}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
>>> import operator
|
||||||
|
|
||||||
|
>>> values = dict(one=1, two=2, three=3)
|
||||||
|
>>> sorted(values.items())
|
||||||
|
[('one', 1), ('three', 3), ('two', 2)]
|
||||||
|
>>> sorted(values.items(), key=lambda item: item[1])
|
||||||
|
[('one', 1), ('two', 2), ('three', 3)]
|
||||||
|
|
||||||
|
>>> get_value = operator.itemgetter(1)
|
||||||
|
>>> sorted(values.items(), key=get_value)
|
||||||
|
[('one', 1), ('two', 2), ('three', 3)]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> key = lambda item: item[1]
|
||||||
|
|
||||||
|
>>> def key(item):
|
||||||
|
... return item[1]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> def key(spam): return spam.value
|
||||||
|
|
||||||
|
>>> key = lambda spam: spam.value
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
::
|
||||||
|
|
||||||
|
Y = lambda f: lambda *args: f(Y(f))(*args)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
def Y(f):
|
||||||
|
def y(*args):
|
||||||
|
y_function = f(Y(f))
|
||||||
|
return y_function(*args)
|
||||||
|
return y
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> Y = lambda f: lambda *args: f(Y(f))(*args)
|
||||||
|
|
||||||
|
>>> def factorial(combinator):
|
||||||
|
... def _factorial(n):
|
||||||
|
... if n:
|
||||||
|
... return n * combinator(n - 1)
|
||||||
|
... else:
|
||||||
|
... return 1
|
||||||
|
... return _factorial
|
||||||
|
>>> Y(factorial)(5)
|
||||||
|
120
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> Y = lambda f: lambda *args: f(Y(f))(*args)
|
||||||
|
|
||||||
|
>>> Y(lambda c: lambda n: n and n * c(n - 1) or 1)(5)
|
||||||
|
120
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> Y = lambda f: lambda *args: f(Y(f))(*args)
|
||||||
|
|
||||||
|
>>> Y(lambda c: lambda n: n * c(n - 1) if n else 1)(5)
|
||||||
|
120
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> quicksort = Y(lambda f:
|
||||||
|
... lambda x: (
|
||||||
|
... f([item for item in x if item < x[0]])
|
||||||
|
... + [y for y in x if x[0] == y]
|
||||||
|
... + f([item for item in x if item > x[0]])
|
||||||
|
... ) if x else [])
|
||||||
|
|
||||||
|
>>> quicksort([1, 3, 5, 4, 1, 3, 2])
|
||||||
|
[1, 1, 2, 3, 3, 4, 5]
|
||||||
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
>>> import heapq
|
||||||
|
|
||||||
|
>>> heap = []
|
||||||
|
>>> heapq.heappush(heap, 1)
|
||||||
|
>>> heapq.heappush(heap, 3)
|
||||||
|
>>> heapq.heappush(heap, 5)
|
||||||
|
>>> heapq.heappush(heap, 2)
|
||||||
|
>>> heapq.heappush(heap, 4)
|
||||||
|
>>> heapq.nsmallest(3, heap)
|
||||||
|
[1, 2, 3]
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> def push(*args, **kwargs):
|
||||||
|
... return heapq.heappush(heap, *args, **kwargs)
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import functools
|
||||||
|
>>> import heapq
|
||||||
|
|
||||||
|
>>> heap = []
|
||||||
|
>>> push = functools.partial(heapq.heappush, heap)
|
||||||
|
>>> smallest = functools.partial(heapq.nsmallest, iterable=heap)
|
||||||
|
|
||||||
|
>>> push(1)
|
||||||
|
>>> push(3)
|
||||||
|
>>> push(5)
|
||||||
|
>>> push(2)
|
||||||
|
>>> push(4)
|
||||||
|
>>> smallest(3)
|
||||||
|
[1, 2, 3]
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> lambda_push = lambda x: heapq.heappush(heap, x)
|
||||||
|
|
||||||
|
>>> heapq.heappush
|
||||||
|
<built-in function heappush>
|
||||||
|
>>> push
|
||||||
|
functools.partial(<built-in function heappush>, [1, 2, 5, 3, 4])
|
||||||
|
>>> lambda_push
|
||||||
|
<function <lambda> at ...>
|
||||||
|
|
||||||
|
>>> heapq.heappush.__doc__
|
||||||
|
'Push item onto heap, maintaining the heap invariant.'
|
||||||
|
>>> push.__doc__
|
||||||
|
'partial(func, *args, **keywords) - new function ...'
|
||||||
|
>>> lambda_push.__doc__
|
||||||
|
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
>>> import operator
|
||||||
|
>>> import functools
|
||||||
|
|
||||||
|
>>> functools.reduce(operator.mul, range(1, 5))
|
||||||
|
24
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> from operator import mul
|
||||||
|
|
||||||
|
>>> mul(mul(mul(1, 2), 3), 4)
|
||||||
|
24
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import operator
|
||||||
|
|
||||||
|
>>> def reduce(function, iterable):
|
||||||
|
... print(f'iterable={iterable}')
|
||||||
|
... # Fetch the first item to prime `result`
|
||||||
|
... result, *iterable = iterable
|
||||||
|
...
|
||||||
|
... for item in iterable:
|
||||||
|
... old_result = result
|
||||||
|
... result = function(result, item)
|
||||||
|
... print(f'{old_result} * {item} = {result}')
|
||||||
|
...
|
||||||
|
... return result
|
||||||
|
|
||||||
|
>>> iterable = list(range(1, 5))
|
||||||
|
>>> iterable
|
||||||
|
[1, 2, 3, 4]
|
||||||
|
|
||||||
|
>>> reduce(operator.mul, iterable)
|
||||||
|
iterable=[1, 2, 3, 4]
|
||||||
|
1 * 2 = 2
|
||||||
|
2 * 3 = 6
|
||||||
|
6 * 4 = 24
|
||||||
|
24
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import operator
|
||||||
|
|
||||||
|
>>> iterable = range(1, 5)
|
||||||
|
|
||||||
|
# The initial values:
|
||||||
|
|
||||||
|
>>> a, b, *iterable = iterable
|
||||||
|
>>> a, b, iterable
|
||||||
|
(1, 2, [3, 4])
|
||||||
|
|
||||||
|
# First run
|
||||||
|
|
||||||
|
>>> a = operator.mul(a, b)
|
||||||
|
>>> b, *iterable = iterable
|
||||||
|
>>> a, b, iterable
|
||||||
|
(2, 3, [4])
|
||||||
|
|
||||||
|
# Second run
|
||||||
|
|
||||||
|
>>> a = operator.mul(a, b)
|
||||||
|
>>> b, *iterable = iterable
|
||||||
|
>>> a, b, iterable
|
||||||
|
(6, 4, [])
|
||||||
|
|
||||||
|
# Third and last run
|
||||||
|
|
||||||
|
>>> a = operator.mul (a, b)
|
||||||
|
>>> a
|
||||||
|
24
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import operator
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
>>> iterable = collections.deque(range(1, 5))
|
||||||
|
|
||||||
|
>>> value = iterable.popleft()
|
||||||
|
>>> while iterable:
|
||||||
|
... value = operator.mul(value, iterable.popleft())
|
||||||
|
|
||||||
|
>>> value
|
||||||
|
24
|
||||||
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
>>> import json
|
||||||
|
>>> import functools
|
||||||
|
>>> import collections
|
||||||
|
|
||||||
|
>>> def tree():
|
||||||
|
... return collections.defaultdict(tree)
|
||||||
|
|
||||||
|
# Build the tree:
|
||||||
|
|
||||||
|
>>> taxonomy = tree()
|
||||||
|
>>> reptilia = taxonomy['Chordata']['Vertebrata']['Reptilia']
|
||||||
|
>>> reptilia['Squamata']['Serpentes']['Pythonidae'] = [
|
||||||
|
... 'Liasis', 'Morelia', 'Python']
|
||||||
|
|
||||||
|
# The actual contents of the tree
|
||||||
|
|
||||||
|
>>> print(json.dumps(taxonomy, indent=4))
|
||||||
|
{
|
||||||
|
"Chordata": {
|
||||||
|
"Vertebrata": {
|
||||||
|
"Reptilia": {
|
||||||
|
"Squamata": {
|
||||||
|
"Serpentes": {
|
||||||
|
"Pythonidae": [
|
||||||
|
"Liasis",
|
||||||
|
"Morelia",
|
||||||
|
"Python"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Let's build the lookup function
|
||||||
|
|
||||||
|
>>> import operator
|
||||||
|
|
||||||
|
>>> def lookup(tree, path):
|
||||||
|
... # Split the path for easier access
|
||||||
|
... path = path.split('.')
|
||||||
|
...
|
||||||
|
... # Use `operator.getitem(a, b)` to get `a[b]`
|
||||||
|
... # And use reduce to recursively fetch the items
|
||||||
|
... return functools.reduce(operator.getitem, path, tree)
|
||||||
|
|
||||||
|
|
||||||
|
>>> path = 'Chordata.Vertebrata.Reptilia.Squamata.Serpentes'
|
||||||
|
>>> dict(lookup(taxonomy, path))
|
||||||
|
{'Pythonidae': ['Liasis', 'Morelia', 'Python']}
|
||||||
|
|
||||||
|
# The path we wish to get
|
||||||
|
|
||||||
|
>>> path = 'Chordata.Vertebrata.Reptilia.Squamata'
|
||||||
|
>>> lookup(taxonomy, path).keys()
|
||||||
|
dict_keys(['Serpentes'])
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> fold_left = lambda iterable, initializer=None: functools.reduce(
|
||||||
|
... lambda x, y: function(x, y),
|
||||||
|
... iterable,
|
||||||
|
... initializer,
|
||||||
|
... )
|
||||||
|
|
||||||
|
>>> fold_right = lambda iterable, initializer=None: functools.reduce(
|
||||||
|
... lambda x, y: function(y, x),
|
||||||
|
... reversed(iterable),
|
||||||
|
... initializer,
|
||||||
|
... )
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
>>> import operator
|
||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
# Sales per month
|
||||||
|
|
||||||
|
>>> months = [10, 8, 5, 7, 12, 10, 5, 8, 15, 3, 4, 2]
|
||||||
|
>>> list(itertools.accumulate(months, operator.add))
|
||||||
|
[10, 18, 23, 30, 42, 52, 57, 65, 80, 83, 87, 89]
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> a = range(3)
|
||||||
|
>>> b = range(5)
|
||||||
|
>>> list(itertools.chain(a, b))
|
||||||
|
[0, 1, 2, 0, 1, 2, 3, 4]
|
||||||
|
|
||||||
|
|
||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> iterables = [range(3), range(5)]
|
||||||
|
>>> list(itertools.chain.from_iterable(iterables))
|
||||||
|
[0, 1, 2, 0, 1, 2, 3, 4]
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> list(itertools.compress(range(1000), [0, 1, 1, 1, 0, 1]))
|
||||||
|
[1, 2, 3, 5]
|
||||||
|
|
||||||
|
|
||||||
|
>>> primes = [0, 0, 1, 1, 0, 1, 0, 1]
|
||||||
|
>>> odd = [0, 1, 0, 1, 0, 1, 0, 1]
|
||||||
|
>>> numbers = ['zero', 'one', 'two', 'three', 'four', 'five']
|
||||||
|
|
||||||
|
# Primes:
|
||||||
|
|
||||||
|
>>> list(itertools.compress(numbers, primes))
|
||||||
|
['two', 'three', 'five']
|
||||||
|
|
||||||
|
# Odd numbers
|
||||||
|
|
||||||
|
>>> list(itertools.compress(numbers, odd))
|
||||||
|
['one', 'three', 'five']
|
||||||
|
|
||||||
|
# Odd primes
|
||||||
|
|
||||||
|
>>> list(itertools.compress(numbers, map(all, zip(odd, primes))))
|
||||||
|
['three', 'five']
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> list(itertools.dropwhile(lambda x: x <= 3, [1, 3, 5, 4, 2]))
|
||||||
|
[5, 4, 2]
|
||||||
|
|
||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> list(itertools.takewhile(lambda x: x <= 3, [1, 3, 5, 4, 2]))
|
||||||
|
[1, 3]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> list(itertools.islice(itertools.count(), 10))
|
||||||
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
|
||||||
|
>>> list(itertools.islice(itertools.count(), 5, 10, 2))
|
||||||
|
[5, 7, 9]
|
||||||
|
|
||||||
|
>>> list(itertools.islice(itertools.count(10, 2.5), 5))
|
||||||
|
[10, 12.5, 15.0, 17.5, 20.0]
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
>>> import operator
|
||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> words = ['aa', 'ab', 'ba', 'bb', 'ca', 'cb', 'cc']
|
||||||
|
|
||||||
|
# Gets the first element from the iterable
|
||||||
|
|
||||||
|
>>> getter = operator.itemgetter(0)
|
||||||
|
|
||||||
|
>>> for group, items in itertools.groupby(words, key=getter):
|
||||||
|
... print(f'group: {group}, items: {list(items)}')
|
||||||
|
group: a, items: ['aa', 'ab']
|
||||||
|
group: b, items: ['ba', 'bb']
|
||||||
|
group: c, items: ['ca', 'cb', 'cc']
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> import operator
|
||||||
|
>>> import itertools
|
||||||
|
|
||||||
|
>>> words = ['aa', 'bb', 'ca', 'ab', 'ba', 'cb', 'cc']
|
||||||
|
|
||||||
|
# Gets the first element from the iterable
|
||||||
|
|
||||||
|
>>> getter = operator.itemgetter(0)
|
||||||
|
|
||||||
|
>>> for group, items in itertools.groupby(words, key=getter):
|
||||||
|
... print(f'group: {group}, items: {list(items)}')
|
||||||
|
group: a, items: ['aa']
|
||||||
|
group: b, items: ['bb']
|
||||||
|
group: c, items: ['ca']
|
||||||
|
group: a, items: ['ab']
|
||||||
|
group: b, items: ['ba']
|
||||||
|
group: c, items: ['cb', 'cc']
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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'
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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')
|
||||||
@@ -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'
|
||||||
@@ -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
Reference in New Issue
Block a user