Python best practices

by leonardo maffi

Version 1.12, May 14 2008

Sometimes even good programmers at their first tries of Python use less than optimal solutions and language constructs. In the years Python has accumulated few redundancies and few warts (and some of them will be removed with Python 3.0), but it's a generally clean language still, so with a page like this you can avoid the most common ones. This page hopes to be really simple and short enough, you can find explanations online elsewhere.

For some things I may be wrong, but this page comes from some experience, so when you don't agree with me, I suggest you to go look for the truth inside many newsgroups and Web pages, just don't assume you are right. If you find I am wrong, or you have questions or comments my email address can be found in my Home Page, I'll glady improve this page and learn from my mistakes.

[Go back to the index (email in the Home Page)]

Bad
Good
x=5
print x,x*x+1
x = 5
print x, x*x + 1
def Function ( x ): ... def function(x): ...
class fooclass: ... class Fooclass(object): ...
d = dict() frequences = {}
list = [1, 2, 3]
dict = {'alpha': 0x1234}
sum = x + y
# Do not shadow builtin names
values = [1, 2, 3]
symbol_address = {'alpha': 0x1234}
tot = x + y
if ( (x==8) and (y>5) ): ... if x == 8 and y > 5: ...
while (x<5): ... while x < 5: ...
"some string" and 'some string' and
""some string""" and '''some string'''
are the same string.
mapping = { 5 :"5", 6:"6" } mapping = {5 : "5", 6 : "6"}
mapping = {5 : "5", 6 : "6"}
if mapping.has_key(6): ...
mapping = {5 : "5", 6 : "6"}
if 6 in mapping: ...
def function( x, l = [] ): ... # Generally don't use mutables as a default
def function(x, l=None): ...
    if l is None:
        l = []
if x == None: ... if x is None: ...
x = 1
if z > 5:
  var1 = 55
# Always use 4 spaces as indent
# (Or always a Tab, but it's less good)
x = 1
if z > 5:
    var1 = 55
mapping = {5 : "5", 6 : "6"}
for key, val in mapping.items(): ...
for key in mapping.keys(): ...
# Use iter* methods when possible
mapping = {5 : "5", 6 : "6"}
for key, val in mapping.iteritems(): ...
for key in mapping: ...
for i in range(10, 2000): ... for i in xrange(10, 2000): ...
# Use this to denote the code that has to
# run when a module is executed and not
# imported:
if __name__ == '__main__':
# Python profiler:
python -m profile -o stats myscript.py
import pstats
p = pstats.Stats('stats'); p.sort_stats('time').print_stats(15)
For source code with not 7-bit ASCII
add this on top:
# coding: latin
al = [1, 2, 3]
for i in xrange(len(al)-1, -1, -1):
    del al[i]
al = [1, 2, 3]
del al[:]
# But often if speed isn't critical you
# can just use:
al = []
repeat
    xxx
until yyy
# Equals to:
while True
    xxx
    if yyy: break
# To add a zip file containing modules
# to the search path:
sys.path.append("some.zip")
a = 5
b = 6
a = aux
a = b
b = aux
a = 5
b = 6
a, b = b, a # Swap
if x < 10 and x > 2: ... if 2 < x < 10: ...
a = 5
b = 5
c = 5
a = b = c = 5
if x == 1: y = fun1(x)
else if x == 2: y = fun2(x)
else if x == 3: y = fun3(x)
else: y = None
if x == 1: y = fun1(x)
elif x == 2: y = fun2(x)
elif x == 3: y = fun3(x)
else: y = None
# Sometimes it's even better to use a dict:
funs = {1:fun1, 2:fun2, 3:fun3}
y = funs.get(x, lambda x:None)(x)
mapping = {5 : "5", 6 : "6"}
for key in mapping.iterkeys(): ...
mapping = {5 : "5", 6 : "6"}
for key in mapping: ...
al = [1, 2, 3]
for i in xrange(len(al)):
    print al[i]
al = [1, 2, 3]
for el in al:
    print el
al = [1, 2, 3]
for i in xrange(len(al)-1, -1, -1):
    print al[i]
al = [1, 2, 3]
for el in reversed(al):
    print el
class Test(object):
    def __init__(I, x): ...
class Test(object):
    def __init__(self, x): ...
# Compute the sum of the ...
def sum_of(x, y, z): ...
def sum_of(x, y, z): ...
    """Compute the sum of the ..."""
from operator import add
sl = ["ab", "cd", "ef"]
all = ""
for s in sl:
    all += s
# Or:
sl = ["ab", "cd", "ef"]
all = reduce(lambda x,y: x+y, sl, "")
sl = ["ab", "cd", "ef"]
all = "".join(sl)
a = "this isn't a word, right?"
a = a.replace("'", " ")
a = a.replace(".", " ")
a = a.replace("?", " ")
a = a.replace(",", "")
from string import maketrans
tab = maketrans("'.?", " ")
a = "this isn't a word, right."
afilt = a.translate(tab, ",")
values = ["stop",0,0] values = ["stop", 0, 0]
def mul(x, y): return x*y
l = [2, 3]
print apply(mul, l)
def mul(x, y): return x*y
l = [2, 3]
print mul(*l)
vals = [2, 3, -5, 0]
result = []
for el in vals:
    if el > 0:
        result.append(el * el)
vals = [2, 3, -5, 0]
result = [el * el for el in vals if el > 0]
l = [0] * 4
m = [l] * 4
m[1][1] = 5
print m
# One correct way to create a matrix:
m = [[0] * 4 for _ in xrange(4)]
m[1][1] = 5
print m
a = 1
print a / 2, a / float(2)
# A kind of alternative:
from __future__ import division
a = 1
print a // 2, a / 2
class Foo(object):
    def __init__(self, x, y, z):
        self.x_public = x
        self.y_private = y
        self.z_veryprivate = z
    def getx(self):
        return self.x_public
print Foo(1, 2, 3).getx()

# Generally getters and setters are not used.
# Instance names with _ before are private
# Instance names with __ before are reserved,
#   and they receive name mangling.
class Foo(object):
    def __init__(self, x, y, z):
        self.x_public = x
        self._y_private = y
        self.__z_veryprivate = z
print Foo(1, 2, 3).x_public

finder = re.compile("^\s*([\[\]])\s*([-+]?\d+)
\s*,\s*([-+]?\d+)\s*([\[\]])\s*$")
finder = re.compile(r"""
    ^ \s*         # start at beginning+ opt spaces
    ( [\[\]] )    # Group 1: opening bracket
    \s*           # optional spaces
    ( [-+]? \d+ ) # Group 2: first number
    \s* , \s*     # opt spaces+ comma+ opt spaces
    ( [-+]? \d+ ) # Group 3: second number
    \s*           # opt spaces
    ( [\[\]] )    # Group 4: closing bracket
    \s* $         # opt spaces+ end at the end
    """, flags=re.VERBOSE)
def function(data):
    """A comment"""
    ...implementation...
# Use doctests (or module tests):
def function(data):
    """A comment

    >>> function()
    None
    >>> function(1)
    result1
    >>> function("a")
    Traceback (most recent call last):
      ...
    TypeError
    """
    ...implementation...

if __name__ == "__main__":
    import doctest
    doctest.testmod()
    print "Tests done."

x = (1, 2, 6, 55, 63, 96, 125, 256, \
     301, 456, 958, 1256, \
     1359, 2568, 3597)
x = (1, 2, 6, 55, 63, 96, 125, 256,
     301, 456, 958, 1256,
     1359, 2568, 3597)
# Too much long lines must be broken with \
# but \ isn't necessary inside () [] {}
from Tkinter import *
from mymodule import *
import Tkinter as tk
from mymodule import fun1, fun2, Class1
import psyco
psyco.bind(myfun1)
a = [3.56, 2.12]
try:
    import psyco
    # Psyco classes may be very useful
    from psyco.classes import __metaclass__
    psyco.bind(myfun1)
except ImportError: pass
# Using psyco array.array of double and
# signed long become very fast
import array
a = array.array("d", [3.56, 2.12])
  # to print strings without spaces between:
from sys import stdout
stdout.write(string1)
stdout.write(string2)
words = ['me', 'do' 'bye', 'taz', 'foo', 'bar'] words = 'me do bye taz foo bar'.split()
# sorting on the second item of the tuple
# try to remove the i index from the temporary tuples
lp = [(5J,"b"),(2J,"c"),(3+1J,"a"),(1+2J,"a")]
lp2 = [(c, i, n) for i,(n, c) in enumerate(lp)]
lp2.sort()
print [(n, c) for (c, i, n) in lp2]
from operator import itemgetter
lp = [(5J, "b"), (2J, "c"), (3+1J, "a"), (1+2J, "a")]
print sorted(lp, key=itemgetter(1))
vals = [5, 7 ,8]
tot = -2.0
for v in vals:
    tot += v
vals = [5, 7 ,8]
tot = sum(vals, -2.0)
ll = [[1, 2, 3], [4], [5, 6]]
print sum(ll, [])
data = [[1, 2, 3], [4], [5, 6]]
result = []
for sublist in data:
    result.extend(sublist)
# Or even, for max speed
from itertools import imap
data = [[1, 2, 3], [4], [5, 6]
result = [None] * sum(imap(len, data))
pos = 0
for sublist in data:
    lensl = len(sublist)
    result[pos : pos+lensl] = sublist
    pos += lensl
print "%s %s" % (string1, string2)
print '"' + chr(c) + '":', freq[c]
print string1, string2
print '"%c": %d' % (c, freq[c])
[' ', c][c.isalpha()] # For Python V.2.5+:
(c if c.isalpha() else ' ')
# How to invert string, lists, etc.
alist[::-1]
astring[::-1]
# To negate (inplace) each second
#  element of alist:
result = []
for (i, v) in enumerate(alist):
    # faster than i % 2
    if i & 1 == 0:
        result.append(v)
    else:
        result.append(-v)
alist[:] = result
from operator import neg
alist[1::2] = map(neg, alist[1::2])

# Or a bit slower but easier to read:
alist[1::2] = [-el for el in alist[1::2]]
# To shallow copy a list or dict:
# (tuples don't need to be copied)
newlist = list(alist)
newdict = dict(adict)
# Old syntax to copy a list:
newlist = list[:]
import sys
sys.exit()
# To stop a console program:
raise SystemExit
if type(s) == type(""): ...
if type(seq) == list or \
   type(seq) == tuple: ...
if isinstance(s, basestring): ...
if isinstance(seq, (list, tuple)): ...
# Or even:
if hasattr(seq, "__getitem__"): ...
# All uppercase for variables that don't change
CONSTANT1 = 10
ANOTHER_CONSTANT = 20
var1 = 30
another_variable = 40
name1 = 5; name2 = 20; print name2
a = 1
b = 2
c = 3
name1 = 5
name2 = 20
print name2
a, b, c = 1, 2, 3
prima = 10
rossa = 20
léger = 30
# English only for names:
first = 10
red = 20
light = 30
__del__ method of classes is
usually undefined.
try:
    fin = file("absent_file.txt")
except:
    ...
try:
    something()
except:
    ...
# Always specify what exception to catch
try:
    fin = file("absent_file.txt")
except IOError:
    ...
try:
    something()
except someException:
    ...
except ImportError, IOError: ... except (ImportError, IOError): ...
bytes = array.array('B', [0] * nbytes)
# Or:
from itertools import repeat
bytes = array.array('B', repeat(0, nbytes))
bytes = array.array('B', [0]) * nbytes
freqs = {}
for c in "abracadabra":
    try:
        freqs[c] += 1
    except:
        freqs[c] = 1
freqs = {}
for c in "abracadabra":
    if c in freqs:
        freqs[c] += 1
    else:
        freqs[c] = 1

# Or even better on Python 2.5:
from collections import defaultdict
freqs = defaultdict(int)
for c in "abracadabra":
    freqs[c] += 1

someitems = set([1, 2, 3])
somemap = {1:2, 3:4, 5:6}
print list(someitems)[0]
print list(somemap)[0]

someitems = set([1, 2, 3])
somemap = {1:2, 3:4, 5:6}
print iter(someitems).next()
print iter(somemap).next()

Thank to Mark Dufour for fixing few errors, and Francesco Brasini for another bug spotted.
See PEP 8 too for more style guides for Python code: http://www.python.org/dev/peps/pep-0008/

[Go back to the index (email in the Home Page)]