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
. # path to venv
echo ${VIRTUAL_ENV} -V
python -V
pip
# install pip if missing
-m ensurepip --upgrade
python --upgrade pip
pip install
# list the active configuration (site in /path/to/venv-3.11.3/pip.conf)
pip config debuglist -v
pip config # configure virtual environment ("site")
--site list
pip config --site get command.option
pip config --site set command.option value
pip config --site unset command.option
pip config
# exit virtual environment
deactivate
1.2 Cheat sheet
# Start HTTP server on port 9000 and share the current directory
python -m http.server 9001
# in python2
python -m SimpleHTTPServer 9001
help(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
= [1,2]
l1 = [2,3]
l2 print( [*l1, *l2] ) # [1, 2, 2, 3]
# splice first item to end of list
= [1,2,3,4]
l print( [*l[1:], l[0]] ) # [2, 3, 4, 1]
# shallow merge dictionaries
= {'a':'A', 'b': 'B'}
d1 = {'b':'BBB'} # second dictionary overwrites 'b'
d2 print( {**d1, **d2} ) # {'a': 'A', 'b': 'BBB'}
# shallow merge dictionaries since Python 3.9
print( d1 | d2 ) # {'a': 'A', 'b': 'BBB'}
# multiple assignment
= 1, 2
a, b = (1, 2)
(a, b) = (0,1), # trailing comma makes single item tuple; a=(0,1)
a, = [(0,1)] # instead of trailing comma; a=(0,1)
[a] = (-1,-2, (1, 2, 3))
a,b, (x, y, z) = [1, 2]
a, b = 1, 2
[a, b] *rest = [1,2,3] # a=1, rest=[2,3]
a, = "AB" # a='A'; b='B'
a, b = "A,B,C".split(',')
a, b, c = sys.argv
_, a, b = b,a # swap a,b
# avoid creating global variables
def main():
=1
x# module name when invoked directly instead of imported
if __name__ == "__main__": main()
= type(True)
t1 = int
t2 assert issubclass(t1, t2), f"{t1!r} is not subclass of {t2!r}"
= print
obj = type(print)
t3 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
1,2, other=3) # 1 2 3
f(1,2) # 1 2 999 f(
Function that requires keyword arguments for every parameter after *
:
def f(pos1, *, arg1, arg2):
pass
1, arg1=2 ,arg2=3) # OK
f(1, 2 ,3) # TypeError: f() takes 1 positional argument but 3 were given f(
Function that takes any keyword as argument (received as a dictionary):
def f(**kwargs):
print( ", ".join(
f"{key}: {value}" for key, value in kwargs.items()
) )
="Bob", hat="Black") # name: Bob, hat: Black
f(name
# unpack dictionary into keyword arguments
= { "name": "Alice", "hat": "grey" }
args **args) # name: Alice, hat: grey f(
1.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:B
1.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
= {'a': 'A', 'b': 'B'}
d = { k+'!':v+'!' for k,v in d.items() if v!='B'}
new_dict print(new_dict) # {'a!': 'A!'}
if any(
== 3
n for n in [1,2,3]
):print("List contained 3")
if all(
< 10
n 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( [[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 method sorted_list
1.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 a=1
f() @dummy_decorator(a=5)
def g(): pass
# g a=5 g()
Any 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.
= dict()
d
def register(func):
__name__] = func
d[func.return func # if not returned here, the function becomes None
@register
def f(arg):
print(f"Hello, {arg}")
"f"]("World") # call f d[
import 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")
# still callable; also runs at exit f()
1.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)
= func(*args, **kwargs)
value 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')
1,2,3, x=5) f(
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):
self, func)
functools.update_wrapper(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
# 1
f() # 2 f()
1.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/ SomeProject
Use 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
= ["A"] __all__
# 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
= os.path.basename(sys.argv[0])
cmd
# convert from stdin based on invoking command
if cmd=="jy":
=sys.stdout, indent=2)
yaml.safe_dump(json.load(sys.stdin), streamelif cmd=="yj":
=sys.stdout, indent=2)
json.dump(yaml.safe_load(sys.stdin), fpelif cmd=="yy":
=sys.stdout, indent=2)
yaml.safe_dump(yaml.safe_load(sys.stdin), streamelif cmd=="jj":
=sys.stdout, indent=2)
json.dump(json.load(sys.stdin), fpelse:
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}' | jy
1.6 String formatting
Conversions:
!r
converts withrepr(object)
which creates a printable representation.!s
converts withstr(object)
which creates a string version of object.!a
converts withascii(object)
which create a printable representation with escaped non-ASCII characters.
1.6.1 String interpolation (f-strings)
= "Hello"
first = "World"
second 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-08
1.6.2 Template strings
https://docs.python.org/3/library/string.html#template-strings
from string import Template
= "$first, $second"
template ="Hello", second="World") Template(template).substitute( first
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():
1.0)
time.sleep(
= time.perf_counter()
start
do_work()= time.perf_counter()
end = end - start
delta
print( f"{do_work.__name__!r} took {delta:.4f} seconds" )
import cProfile
import pstats
with cProfile.Profile() as pr:
do_work()
= pstats.Stats(pr)
stats
stats.sort_stats(pstats.SortKey.TIME)
stats.print_stats()="stats.prof") stats.dump_stats(filename