How to write sane Python ------------------------ BASIC HYGIENE H1. print a copy of PEP8 and burn it unceremoniously H2. require at least python 3.6. Previous versions are unusable. H3. process strings with % or with f-interpolation; do not use .format H4. try to ignore the existence of exceptions as much as possible H5. avoid any kind of boilerplate that helps for 2.7 compatibility H6. avoid brain-damaged stuff like type annotation imports NAMING N1. local variables are single letters. No exceptions. N2. use unicode letters without fear: α, β, γ instead of alpha, beta, gamma N3. function names are long and descriptive N4. global variables have long and uppercase names N5. do not bring new mixed-case symbols into the world N6. either your file is executable or its name ends in .py, but not both N7. arguments cannot have the same name as values: you never see f(x=x) FORMATTING F1. indent with 1 tab per indentation level F2. max 80 characters per line (1 indent counts as 8) F3. max 25 lines per function F4. max 8 local variables per scope F5. strings are always quoted "like this", ... F6. ... except when they are dictionary keys, which are quoted 'thusly' COMMENTS C1. write a single-line comment for each variable, explaining its meaning C2. write a few lines of comment to document each function C3. docstrings are used only for functions intended to be used in another file C4. the docstring does not replace the comment associated to the function. It serves a very different purpose: The docstring is to be read by people who only want to use your function but do not care about the implementaiton. The comment is to be read by the person who implements the function, and it often explains how the function relates to the other pieces of the code. C5. do not put any licensing/copyright boilerplate at the top of the file (if it is really necessary, put it at the bottom of the file) IMPORTING I1. strive to minimize the number of imports I2. imports should be as local as possible (e.g., inside a function) I3. unless your script has code at level 0, global imports are prohibited I4. Comparison of importing styles: BAD: import package as pk # your code becomes un-copypasteable GOOD: import package # code is clear (but may be too verbose) BEST: from package import fu bar # useful for short and function-heavy code EXPORTING E1. do not export visibility of dependencies E2. do not export visubility of unnecessary functions E3. each exported function must have a comprehensive docstring E4. use sys.modules[__name__] = foo to avoid foo.foo stupidity USAGE U1. use string interpolation instead of concatenation U2. avoid os.path.join, use string processing instead U3. in numpy, never use .dot, use always @ U4. use matplotlib for plots, but do not EVER use it for displaying an image U5. tkinter is there, and is useful, and is simple; do not be afraid of it U6. represent graphs as sparse matrices, not as dictionaries^2 SHELL ENVIRONEMENT S1. rm -rf ~/.local ; touch ~/.local ; chmod 000 ~/.local # once and for all S2. mkdir -p ~/local/{bin,share/python,dotlocal} S3. export PATH=~/local/bin:~/local/share/python/bin:$PATH S4. export PYTHONDONTWRITEBYTECODE=1 S5. export PYTHONUSERBASE=~/local/share/python S6. export XDG_DATA_HOME=~/local/share/dotlocal S7. pip install shit # only affects your local user S8. delete $PYTHONUSERBASE regularly and re-install packages as the need arises S9. do not use virtualenvs MATH M1. (Remember N1) Local variable names are single letters. No exceptions. M2. (Remember N2) Do not fear unicode: from math import pi as π M3. (Remember I1) Do not use global imports. Write functions that import what they need to perform their computation. M4. Use dependencies "implicitly": For example, you do not need to import numpy to do most things with numpy arrays. You just need an external function that returns numpy arrays, and work from there using method functions of the array class. This has the advantage that if your arrays become of another type (with a compatible interface), you do not need to change anything. For example, most linear algebra code can be written to work transparently with numpy arrays, sparse matrices, etc. If you are careful, the same numerical code can work with numpy, torch and jax arrays without ever mentioning them. TYPING T1. No PACKAGING P1. your program should be easily usable without need to "install" it P2. ideal case: "wget url://to/myprogram.py ; python myprogram.py" P3. do not force installation of non-essential dependencies. If your program requires a dependency just for a small, optional feature, allow to install it and fail at runtime when the dependency is found missing. P4. pip is shit, but conda and poetry are orders of magnitude worse: avoid them! P5. jupyter notebooks are great for literate programming, but the .ipynb file format is horrific. Use jupytext to distribute your notebooks as plain python files. Check that you can run your notebook by invoking it explicitly from the shell: "python notebook.py". Do away with the .ipynb files entirely.