1 Python notes
1.1 Virtual environment
# create python virtual environment
/usr/bin/python3.11 -m venv --prompt '3.11.3' /path/to/venv-3.11.3
# enter virtual environment
. /path/to/venv-3.11.3/bin/activate
echo ${VIRTUAL_ENV} # path to venv
python -V
pip -V
# install pip if missing
python -m ensurepip --upgrade
pip install --upgrade pip
# list the active configuration (site in /path/to/venv-3.11.3/pip.conf)
pip config debug
pip config list -v
# configure virtual environment ("site")
pip config --site list
pip config --site get command.option
pip config --site set command.option value
pip config --site unset command.option
# exit virtual environment
deactivate1.2 Cheat sheet
# Start HTTP server on port 9000 and share the current directory
python -m http.server 9001
# in python2
python -m SimpleHTTPServer 9001help(print) # show help page on functions
help("ensurepip") # show help page on modules
dir() # print names in the current scope
dir(obj) # print module/object attributes
locals() # returns a dictionary containing the current scope's local variables
vars() # same as locals()
vars(obj) # obj.__dict__
type(obj) # return the type of object
isinstance(obj, class_or_tuple) # return whether an object is an instance or subclass of class
issubclass(cls, class_or_tuple) # return whether a type/class is an instance or subclass of class# shallow merge lists
l1 = [1,2]
l2 = [2,3]
print( [*l1, *l2] ) # [1, 2, 2, 3]
# splice first item to end of list
l = [1,2,3,4]
print( [*l[1:], l[0]] ) # [2, 3, 4, 1]
# shallow merge dictionaries
d1 = {'a':'A', 'b': 'B'}
d2 = {'b':'BBB'} # second dictionary overwrites 'b'
print( {**d1, **d2} ) # {'a': 'A', 'b': 'BBB'}
# shallow merge dictionaries since Python 3.9
print( d1 | d2 ) # {'a': 'A', 'b': 'BBB'}# multiple assignment
a, b = 1, 2
(a, b) = (1, 2)
a, = (0,1), # trailing comma makes single item tuple; a=(0,1)
[a] = [(0,1)] # instead of trailing comma; a=(0,1)
a,b, (x, y, z) = (-1,-2, (1, 2, 3))
a, b = [1, 2]
[a, b] = 1, 2
a, *rest = [1,2,3] # a=1, rest=[2,3]
a, b = "AB" # a='A'; b='B'
a, b, c = "A,B,C".split(',')
_, a, b = sys.argv
a,b = b,a # swap# avoid creating global variables
def main():
x=1
# module name when invoked directly instead of imported
if __name__ == "__main__": main()t1 = type(True)
t2 = int
assert issubclass(t1, t2), f"{t1!r} is not subclass of {t2!r}"
obj = print
t3 = type(print)
assert isinstance(obj, t3), f"{obj!r} is not instance of {t3!r}"1.2.1 Keyword arguments
Function that takes any number of arguments and an optional keyword argument:
def f(*mytuple, other=999):
print(*mytuple, other) # unpack tuple into arguments
f(1,2, other=3) # 1 2 3
f(1,2) # 1 2 999Function that requires keyword arguments for every parameter after *:
def f(pos1, *, arg1, arg2):
pass
f(1, arg1=2 ,arg2=3) # OK
f(1, 2 ,3) # TypeError: f() takes 1 positional argument but 3 were givenFunction that takes any keyword as argument (received as a dictionary):
def f(**kwargs):
print( ", ".join(
f"{key}: {value}" for key, value in kwargs.items()
) )
f(name="Bob", hat="Black") # name: Bob, hat: Black
# unpack dictionary into keyword arguments
args = { "name": "Alice", "hat": "grey" }
f(**args) # name: Alice, hat: grey1.2.2 Loops
# iterate over numbers
for i in range(5): # range(stop)
print(i, end=' ') # 0 1 2 3 4
for i in range(0, 5, 1): # range(start, stop[, step])
print(i, end=' ') # 0 1 2 3 4
# enumerate index+value
for i,val in enumerate(['a','b']): # enumerate(iterable, start=0)
print(i, val, sep=':', end=' ') # 0:a 1:b
# enumerate two iterables (ends at shortest)
for x,y in zip( [1,2],['1','2','3'] ):
print(x, y, sep=':', end=' ') # 1:1 2:2
# dictionary comprehension
for k,v in {'a': 'A', 'b': 'B'}.items():
print(k, v, sep=':', end=' ') # a:A b:B1.2.3 List comprehension / dictionary comprehension
The syntax, where K and V are expressions and if conditional is optional, is:
generator = ( V for v in iterable if conditional )new_list = [ V for v in iterable if conditional ]new_dict = { K: V for k,v in iterable if conditional }
# generator
for i in (n for n in [1,2,3] if n % 2 == 1):
print(i, end=' ') # 1 3
# list comprehension
print( [ v*2 for v in [1,2,3] ] ) # [2, 4, 6]
# dictionary comprehension
d = {'a': 'A', 'b': 'B'}
new_dict = { k+'!':v+'!' for k,v in d.items() if v!='B'}
print(new_dict) # {'a!': 'A!'}if any(
n == 3
for n in [1,2,3]
):
print("List contained 3")
if all(
n < 10
for n in [1,2,3]
):
print("List contained only numbers less than 10")1.2.4 Lambda expressions
for i in map(lambda v: v.encode(encoding="utf-32"), ["a", "A"]):
print(i) # b'\xff\xfe\x00\x00a\x00\x00\x00'
# b'\xff\xfe\x00\x00A\x00\x00\x00'
for i in filter(lambda v: abs(v)==v, [-1, 0, 1]):
print(i, end=' ') # 0 1# using operators module instead of lambdas
from operator import itemgetter,attrgetter,methodcaller
sorted_list = sorted( [[1,1],[0,0]], key=itemgetter(1) ) # get index or key
sorted_list = sorted( [{'a':2}, {'a':1}], key=attrgetter("a") ) # get property
sorted_list = sorted( ["Bob","Alice"], key=methodcaller("casefold") ) # call method1.2.5 Decorators
https://peps.python.org/pep-0318/#examples.
A function decorator overrides the decorated function. The decorator receives the function as an argument. The inner wrapper function uses *args to pack any positional arguments and **kwargs to pack any keyword arguments.
Decorate the inner wrapper function with @functools.wraps to assign attributes (like __name__ and __doc__) from the wrapped function.
import functools
def dummy_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@dummy_decorator
def f(): pass # f = dummy_decorator(f)A decorator with parameters is called with the arguments and it should return an outer wrapper function. The returned outer wrapper is then called with the decorated function. The returned inner wrapper function overwrites the decorated function.
import functools
def dummy_decorator(a=1):
def wrapper_outer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return wrapper_outer
@dummy_decorator(a=5)
def g(): pass # g = (dummy_decorator(a))(g)Alternatively, use partial to return itself with different arguments. When the decorator is called the second time it will be passed the decorated function along with the additional arguments given to partial.
def dummy_decorator(func=None, *, a=1):
# func will be None when decorator is used with arguments.
if func is None:
# return a function that wraps this function with additional arguments.
# when dummy_decorator is called the second time, func will be the decorated function.
return functools.partial(dummy_decorator, a=a)
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(func.__name__, f"a={a}")
return func(*args, **kwargs)
return wrapper
@dummy_decorator
def f(): pass
f() # f a=1
@dummy_decorator(a=5)
def g(): pass
g() # g a=5Any function that accepts a function argument can be used as a decorator. It should return a function to overwrite the decorated one, but it isn’t required.
d = dict()
def register(func):
d[func.__name__] = func
return func # if not returned here, the function becomes None
@register
def f(arg):
print(f"Hello, {arg}")
d["f"]("World") # call fimport atexit
# atexit.register returns the function itself after registering as exit handler.
# arguments can't be passed with this decorator.
@atexit.register
def f():
print("atexit")
f() # still callable; also runs at exit1.2.6 Cache decorator
Cache decorator stores values from previous function calls. @cache memoizes all calls (same as lru_cache(maxsize=None)). @lru_cache stores only the least recently used up to a maximum size.
from functools import cache, lru_cache
# @cache
@lru_cache(maxsize=5)
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
for i in range(400):
print(i, fib(i))
print("done")1.2.7 Debug decorator
import sys
import functools
def debug(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
print(f"{ func.__name__ }({ args }, { kwargs.items() })", file=sys.stderr)
value = func(*args, **kwargs)
print(f"{ func.__name__ } returned { value }", file=sys.stderr)
return value
except Exception as e:
print(f"Exception in {func.__name__}: {e}", file=sys.stderr)
raise
return wrapper
@debug
def f(*args, **kwargs):
raise Exception('a', 'b')
f(1,2,3, x=5)1.2.8 Stateful class decorator
Use functools.update_wrapper to update the __call__ method as a wrapper that looks like the decorated function.
import functools
class C:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.counter = 0
# the wrapper around the decorated function
def __call__(self, *args, **kwargs):
self.counter += 1
print(f"{self.counter}")
return self.func(*args, **kwargs)
@C
def f(): pass
f() # 1
f() # 21.3 Pip
Search packages at https://pypi.org/.
# configure repository "python.repo"
pip config set global.trusted-host python.repo
pip config set global.index https://python.repo/repository/a/b
pip config set global.index-url https://python.repo/repository/a/b
# temporarily override configuration
pip install requests --config-settings pip list # list installed packages
pip list --not-required # list installed packages that aren't dependencies
pip list --outdated
pip show --files PACKAGE # list files of package
pip install requests==2.28.2 # install specific version (with dependencies)
pip show requests
# install with requirements
cat <<<'requests==2.28.2' > requirements.txt
pip install --requirement requirements.txt
# install with constraints
cat <<<'urllib3>=1.26.0' > constraints.txt
pip install --constraint constraints.txt requests
pip check # verify installed packages have compatible dependencies
# output requirements file to stdout containing currently installed packages
pip freeze requests
pip freeze --all
pip uninstall PACKAGE # uninstall package (not its dependencies)
pip uninstall --requirement <(pip freeze PACKAGE) # shallow uninstall of package
pip install pip-autoremove
pip-autoremove --list # list unused dependencies
pip-autoremove --leaves # list packages not used by other packages
pip-autoremove PACKAGE # uninstall package and its dependencies
pip install /path/to/files # install from local path
pip install -e /path/to/files # install from local path as editable
pip install ./downloads/SomeProject-1.0.4.tar.gz
pip install -e 'git+ssh://git.repo/some_pkg.git@master' # specify ref after @
pip install -e 'git+https://git.repo/some_pkg.git#egg=project_name&subdirectory=pkg_dir'
pip install -e 'git+file:///home/user/projects/MyProject'
pip install --index-url http://my.package.repo/simple/ SomeProjectUse pip download to download packages for other systems. pip.pypa.io/en/latest/cli/pip_download/.
1.4 Delegate to Python 2 or 3 module implementations
Module file structure:
mymodule/
├── A.py
├── __init__.py
├── py2/
│ ├── A.py
│ └── __init__.py
└── py3/
├── a.py
└── __init__.py# runs different implementations depending on Python version
python2 -c 'from mymodule import A; print(A.f())' # prints: py2 f
python3 -c 'from mymodule import A; print(A.f())' # prints: py3 f# File: mymodule/__init__.py
__all__ = ["A"]# File: mymodule/A.py
import sys
if sys.version_info >= (3,):
from .py3.a import f as f
else:
from .py2.A import f as f# File: mymodule/py2/A.py
def f():
return "py2 f"# File: mymodule/py3/a.py
def f():
return "py3 f"1.5 JSON/YAML pretty print
Create a file yamljson.py in $PATH with the following code.
#!/usr/bin/python
import sys
import os
import yaml
import json
# get name of invoking command
cmd = os.path.basename(sys.argv[0])
# convert from stdin based on invoking command
if cmd=="jy":
yaml.safe_dump(json.load(sys.stdin), stream=sys.stdout, indent=2)
elif cmd=="yj":
json.dump(yaml.safe_load(sys.stdin), fp=sys.stdout, indent=2)
elif cmd=="yy":
yaml.safe_dump(yaml.safe_load(sys.stdin), stream=sys.stdout, indent=2)
elif cmd=="jj":
json.dump(json.load(sys.stdin), fp=sys.stdout, indent=2)
else:
print("ERROR: invoked as '", cmd, "'")
print("FIX: ln -s jy|yj|yy|jj " + cmd)Create symbolic links in the same directory to match the conditions on the value in argv[0].
ln -s yamljson.py yj # converts YAML to pretty JSON
ln -s yamljson.py jy # converts JSON to pretty YAML
ln -s yamljson.py yy # converts YAML to pretty YAML
ln -s yamljson.py jj # converts JSON To pretty JSON
# usage (JSON to YAML)
echo '{"abc": true}' | jy1.6 String formatting
Conversions:
!rconverts withrepr(object)which creates a printable representation.!sconverts withstr(object)which creates a string version of object.!aconverts withascii(object)which create a printable representation with escaped non-ASCII characters.
1.6.1 String interpolation (f-strings)
first = "Hello"
second = "World"
f"{first}, {second}" # Hello, World# print as key value
f"{first=}, {second = }" # first='Hello', second = 'World'
# call __format__() on argument
f"{1.0/3.0:.2f}" # 0.33
f"{datetime.datetime.utcnow():%Y-%m-%d}" # 2023-02-081.6.2 Template strings
https://docs.python.org/3/library/string.html#template-strings
from string import Template
template = "$first, $second"
Template(template).substitute( first="Hello", second="World")1.6.3 str.format
https://docs.python.org/3/library/string.html#formatstrings.
"Hello, {}".format("World")
"{first}, {second}".format(first="Hello", second="World")1.6.4 printf-style
https://docs.python.org/3/library/stdtypes.html?highlight=sprintf#printf-style-string-formatting.
Syntax is format % values, where format contains %(KEY)type-specifier and values is a dictionary with matching key names.
"%(first)s, %(second)s" % {"first": "Hello", "second": "World"}1.7 Performance counter
import time
def do_work():
time.sleep(1.0)
start = time.perf_counter()
do_work()
end = time.perf_counter()
delta = end - start
print( f"{do_work.__name__!r} took {delta:.4f} seconds" )import cProfile
import pstats
with cProfile.Profile() as pr:
do_work()
stats = pstats.Stats(pr)
stats.sort_stats(pstats.SortKey.TIME)
stats.print_stats()
stats.dump_stats(filename="stats.prof")