Python

Local setup

Install pyenv to switch between Python versions.
Use python3 -m venv .venv to create a local virtual environment.
In zsh, install zsh-autoswitch-virtualenv to autoswitch between local venvs.
To manage local project dependencies use poetry.

Use a specific Python 3 version

pyenv install 3.13        # install latest minor python version
pyenv local 3.13          # use newly installed python version

python3 -m venv .venv     # create a virtualenv
# or mkvenv w/ zsh-autoswitch-virtualenv

Other useful pyenv commands:

pyenv versions      # list installed python versions
pyenv version-name  # see current python version
pyenv which python  # see path to python executable

pyenv uninstall 3.13.4  # remove python version, needs to be specific

Configure Poetry for dependency management

poetry config virtualenvs.create true --local
poetry init

To use poetry only for dependency management in pyproject.toml add:

[tool.poetry]
package-mode = false

To add some deps:

poetry add mkdocs
poetry add -D black isort   # dev deps

Generate a .python-version file

Shouldn't be needed after running pyenv local ... but in any other case:

echo $(pyenv version-name) > .python-version

.python-version is then used by pyenv to automatically switch to correct python version when navigating into the project's folder.

Generate a requirements.txt file

Shouldn't be needed when using poetry but in any other case:

pyenv shell 3.13                    # use specific python version
pip install [DEPENDENCY_1] [...]    # install dependencies
pip freeze > requirements.txt       # freeze requirements

Install dependencies from a requirements.txt file

pip install -r requirements.txt

Containerized Python development

Docs

Package registry

Snippets

#!/usr/bin/env python

Consult object help

help(set)

Parse arguments

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('-i', '--requests-interval', type=float, default=5)
args = parser.parse_args()

print(args.verbose, args.requests_interval)

Reference: - https://docs.python.org/3/howto/argparse.html - https://docs.python.org/3/library/argparse.html

Performance counter

time.perf_counter()

import time
start = time.perf_counter()
# ...
end = time.perf_counter()
print('timing: {time}'.format(time=(end - start)))

Reference count

import ctypes
def ref_count(address):
    return ctypes.c_long.from_address(address).value

Interning

a = 10
b = int('1010', 2)
print(hex(id(a)))
print(hex(id(b)))
a is b # True, in [-5, 256]

a = 500
b = 500
a is b # False

String interning: don't do this unless optimization is needed

import sys
a = 'foo'
b = 'foo'
a is b # True, looks like an identifier so gets interned... but don't count on it

a = 'foo bar'
b = 'foo bar'
a is b # False

a = sys.intern('foo bar')
b = sys.intern('foo bar')
a is b # True

Peephole

def my_func():
    a = 24 * 60
    b = 'foo'
    c = 'foo' * 2
    d = ['foo', 'bar']
    e = ('foo', 'bar')
    f = {'foo', 'bar'}

print(my_func.__code__.co_consts) 
# (None, 24, 60, 'foo', 2, 'bar', 1440, 'foofoo', ('foo', 'bar'))

Unpacking zen

Remove list duplicates

a = [1, 1, 2, 2, 3, 3]
*a, = {*a} # a = [1, 2, 3]

Overwrite dictionary values

a = {'foo': 'bar', 'baz': 'qux'}
b = {'foo': 'quux', 'quuz': 'courge'}
b = {**a, **b} # b = {'foo': 'quux', 'baz': 'qux', 'quuz': 'courge'}

Nested unpacking

a, *b, (c, d, e) = [1, 2, 3, 'XYZ']
# a = 1, b = [2, 3], c = 'X', d = 'Y', e = 'Z'