This guide follows some ideas from The Hitchhiker's Guide To Python
and other ideas from the internet and myself. It is a work in progress.
PEP 8
: The Python Style Guide. Read this. All of it. Follow it.
PEP 257
: Docstring
Conventions. Gives guidelines for semantics and conventions associated with Python docstrings
.
This section contains only IDE and editors that I have Python experience with. Note that this section is incomplete.
Make vim
PEP8
compatible. Best if vim
was compiled with +python
.
set textwidth=79 " lines longer than 79 columns will be broken
set shiftwidth=4 " operation >> indents 4 columns; << unindents 4 columns
set tabstop=4 " a hard TAB displays as 4 columns
set expandtab " insert spaces when hitting TABs
set softtabstop=4 " insert/delete 4 spaces when hitting a TAB/BACKSPACE
set shiftround " round indent to multiple of 'shiftwidth'
set autoindent " align the new line indent with the previous line
If you also use vim for another language, use the [ident] plugin (http://www.vim.org/scripts/script.php?script_id=974).
Improved syntax highlighting.
PEP8
compliancepycodestyle
PEP8
compliance Pyflakes install vim-flake8 to check PEP8 from within vim
. Map function Flake8
to a hotkey.
Check PEP8 (Pyflake) on every save, edit .vimrc
: autocmd BufWritePost*.py call Flake8()
.
Show PEP8 errors and warnings with syntastic in the quickfix window.
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
let g:syntastic_auto_loc_list=1
let g:syntastic_loc_list_height=5
Use the Python mode: Python code checking with pylint, pyflakes, pycodestyle, or mccabe; refactoring, autocompletion with Rope; virtualenv support, documentation search.
Use SuperTab for code completion with the tab key.
Install pip
if not already done.
IPhthon should be used interactively with the features: shell, web-based notebook, data visualization, GUI toolkits, parallel execution.
pip install ipython # base install only
pip install ipython[all] # notebook qtconsole, tests, ...
Use virtual environments on a per project base (also with pip
).
Bash: (for pip install
, this will make pip
require a virtual environment)
export PIP_REQUIRE_VIRTUALENV=true
See this example environment.
#!/usr/bin/zsh
VER=3.10.2
# Disable the creation if bytecode (should be enabled)
# export PYTHONDONTWRITEBYTECODE=1
# pip only work with virtual enviroments (prevents accidental global installs)
# Or edit pip.conf, pip.ini
export PIP_REQUIRE_VIRTUALENV=true
# cache often used libraries, or edit pip.conf, pip.ini
if [ ! -d $HOME/.pip/cache ]; then mkdir -p $HOME/.pip/cache; fi
export PIP_DOWNLOAD_CACHE=$HOME/.pip/cache
# To install globally use 'gpip'
gpip() {
PIP_REQUIRE_VIRTUALENV="" pip "$@"
}
LOC=/usr/local/bin
BIN=$LOC/python3.10
DIR=/srv/build/Python-$VER
if [ -f $BIN ];then
#echo "BIN [$BIN] exists"
export PATH=$LOC:$PATH
alias -g python3="$BIN"
fi
This is the layout of the project sample
README.rst
LICENSE # full license text
Makfile # not mandatory
setup.py # package distribution management
requirements.txt # pip requirements file
sample/__init__.py # 'sample' is the project name, mostly empty
sample/core.py # 'sample' is the project name
sample/helpers.py # 'sample' is the project name
docs/conf.py # package reference documentation
docs/index.rst # package reference documentation
tests/test_basic.py
tests/test_advanced.py
sample
(and replace this with a real name) and do not use src
or python
.init:
pip install -r requirements.txt
test:
py.test tests
.PHONY:init test
clean:
# remove bytecode
find . -type f -name "*.py[co]" -delete -or -type d -name "__pycache__" -delete
Use a simple and explicit path modification to resolve the package properly
Individual tests should have an import context, create a tests/context.py
file:
import os
import sys
sys.path.insert(0,os.path.abspath(os.path.join(os.path.dirname(__file__),'..')))
import sample
Alternatively this can be done in tests/__init__.py
.
from.context import sample
from module import *
from module import function
might be OK"""Bad:"""
from MODULE import *
"""
FUNCTION(PARAMETER)
"""
"""Better:"""
from MODULE import FUNCTION
"""
FUNCTION(PARAMETER)
"""
"""Best:"""
import MODULE
"""
MODULE.FUNCTION(PARAMETER)
"""
import dir1.dir2.module as mod
.__init__.py
file is a package
__init__.py
__init__.py
emtpyThe command import pack.module
will look for the file pack/__init.py__
and execute all top level statements and then look for pack/module.py
and execute all top level statements.
Use stateless functions, if possible, because pure functions are:
"""Bad"""
nums = ""
for n in range(20):
nums += str(n) # slow and inefficient
print nums
"""Good"""
nums = []
for n in range(20):
nums.append(str(n))
print "".join(nums) # much more efficient
"""Better"""
nums = [str(n) for n in range(20)]
print "".join(nums)
"""Best"""
nums = map(str, range(20))
print "".join(nums)
foo = 'foo'
bar = 'bar'
foobar = foo + bar # This is good
foo += 'ooo' # This is bad, instead you should do:
foo = ''.join([foo, 'ooo'])
foo = 'foo'
bar = 'bar'
foobar = '%s%s' % (foo, bar) # It is OK
foobar = '{0}{1}'.format(foo, bar) # It is better
foobar = '{foo}{bar}'.format(foo=foo, bar=bar) # It is best
"""Bad"""
def make_complex( *args):
x, y = args
return dict( **locals())
"""Good"""
def make_complex(x, y):
return {'x': x, 'y': y}
Easy to read (the name and arguments need no explanations)
Easy to change (adding a new keyword argument does not break other parts of the code)
print_at(x,y)
draw_line(x1,y1,x2,y2)
send(message, to, cc=None, bcc=None)
"""Bad"""
send(message, *args)
send('Hello', ['Bilbo', 'Frodo', 'Sauron']
"""Better"""
send(message, recipients)
send('Hello', ['Bilbo', 'Frodo', 'Sauron']
Although there is usually one way to do it. It seems that when it comes to documentation, there are more.
Python is no exception, suggesting that a README
file be maintained for project documentation rather than an INSTALL
document, since installation methods are usually known and similar. The Python manual suggests the presence of a LICENSE
file.
If the project gets bigger and the README
gets too long (only then), parts of the README
might be moved to a TODO
file and a CHANGELOG
file.
For the format reStructuredText
or Markdown
is suggested.
This is usually sufficient for small and medium-sized projects. Often, the death knell of an emerging open source software project comes when the project reaches a size where a README is no longer sufficient. Then a wiki, or even a homepage, is suggested, and political factions form, sometimes engaging in fierce bike-shed color wars. The following short section is for those situations.
Shpinx is a popular Python documentation tool that uses reStructuredText and spits out HTML and PDF.
For other parts, it is advisable to document the source code already. Python can be well documented using docstrings
(PEP 0257#specification
) in favor of block comments (PEP 8#comments
).
A docstring
comment is basically a multi-line comment with three quotes around it, and in some cases they can be used to supplement unit tests.
def add_two_numbers(x, y):
"""
SUM = add_two_numbers(NUMBER_0, NUMBER_1)
- Combine two NUMBERS via the operation of addition
- Require a two NUMBERS: NUMBER_0, NUMBER_1
- Return the sum of 2 NUMBERS
"""
return x + y
Of course in such an obvious case a one line docstring
comment is apropriate
def add_two_numbers(x, y):
"""Add two numbers and return the sum."""
return x + y
*.pyc
files to the source code repositoryexport PYTHONDONTWRITEBYTECODE=1
(but probably should not)*.pyc
*.pyo
*.pyd
__pycache__
Or
# Will match .pyc, .pyo and .pyd files.
*.py[cod]
# Exclude the whole folder
__pycache__/
Version | Date | Notes |
---|---|---|
0.1.2 | 2023-05-11 | Improve writing, add environemnt, add link. |
0.1.1 | 2022-06-09 | Shell->bash, +history, documtation |
0.1.0 | 2020-01-18 | Initial release |