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 |