This style guide aims to document my preferred style for writing Python code.
It is based on Python PEP 8. Portions of this guide borrow heavily from:
- Google: C++ and Python style guides.
- Airbnb: Ruby style guide.
- Trey Hunner: Python style guide.
The ultimate goal of this guide is having code that is clean, consistent, and efficient. Some parts of the guide are opinionated and meant to be strictly followed to preserve consistency when writing new code.
- Layout
- Commenting
- Naming Conventions
- Strings
- Regular Expressions
- Conditionals
- Recommendations
- Be Consistent
- A Foolish Consistency is the Hobgoblin of Little Minds
-
Use 4 spaces per indentation level. [link]
-
Continuation lines should align wrapped elements either vertically using Python's implicit line joining inside parentheses, brackets and braces, or using a hanging indent.[link]
# Yes # Aligned with opening delimiter. def long_function_name(var_one, var_two, var_three, var_four): # Hanging indents should add a level. foo = long_function_name( var_one, var_two, var_three, var_four, )
# No # Arguments on first line are forbidden when not using vertical alignment. foo = long_function_name(var_one, var_two, var_three, var_four)
# Good when it fits the line length limit def create_translation(phrase_id, phrase_key, target_locale): ... translation = create_translation(phrase_id, phrase_key, target_locale) # Good, but use it only for function definitions def create_translation(phrase_id, phrase_key, target_locale, value, user_id, do_xss_check): ... # Good, stick to one argument or element per line # This applies to lists, tuples, sets, dictionaries, function calls translation = create_translation( phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, )
-
Never leave trailing whitespace. [link]
-
Avoid extraneous whitespace in the following situations.[link]
-
Don't use spaces around the = sign when used to indicate a keyword argument or a default parameter value.[link]
# No def func(arg1, arg2 = 2): # Yes def func(arg1, arg2=2):
-
Compound statements (multiple statements on the same line) are generally discouraged.[link]
# Yes if foo == 'blah': do_blah_thing() do_one() do_two() do_three() # Rather not if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three() # Definitely not if foo == 'blah': do_blah_thing() else: do_non_blah_thing() try: something() finally: cleanup() do_one(); do_two(); do_three(long, argument, list, like, this) if foo == 'blah': one(); two(); three()
-
Keep each line of code to a readable length. Unless you have a reason to, keep lines to fewer than 120 characters.[link]
Long lines mean you are doing too much on a single line.
-
The closing brace/bracket/parenthesis on multiline constructs should be lined up under the first character of the line that starts the multiline construct. [link]
# Yes my_list = [ 'item 1', 'item 2', 'item 3', 'itme 4', 'item 5', 'item 6', ] result = some_function_that_takes_arguments( 'arg1', 'arg2', 'arg3', 'arg4', ) # No my_list = [ 'item 1', 'item 2', 'item 3'] my_list = [ 'item 1', 'item 2', 'item 3' ]
-
Add line break before binary operators. [link]
This applies to
and
andor
as well# No: operators sit far away from their operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest) # Yes: easy to match operators with operands income = ( gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest )
-
Add line breaks when it improves readability. For instance, add line breaks between the mapping, looping, and (optional) conditional parts of a comprehension. [link]
# No employee_hours = [schedule.earliest_hour for employee in self.employess for schedule in employee.schedules] return min(h for h in employee_hours if h is not None) # Yes employee_hours = [ schedule.earliest_hour for employee in self.employess for schedule in employee.schedules ] return min( hour for hour in employee_hours if hour is not None )
-
For a very short comprehension, it is acceptable to use just one line of code.[link]
sum_of_squares = sum(n**2 for n in numbers)
-
Trailing commas are usually optional, except they are mandatory when making a tuple of one element.[link]
# Yes FILES = ('setup.cfg',) # OK, but confusing FILES = 'setup.cfg',
-
When trailing commas are redundant, they are often helpful when a version control system is used, when a list of values, arguments or imported items is expected to be extended over time. The pattern is to put each value (etc.) on a line by itself, always adding a trailing comma, and add the close parenthesis/bracket/brace on the next line. However it does not make sense to have a trailing comma on the same line as the closing delimiter (except in the above case of singleton tuples).[link]
# Yes FILES = [ 'setup.cfg', 'tox.ini', ] initialize( FILES, error=True, ) # No FILES = ['setup.cfg', 'tox.ini',] initialize(FILES, error=True,)
-
Surround top-level function and class definitions with two blank lines.[link]
-
Method definitions inside a class are surrounded by a single blank line.[link]
-
Use blank lines in functions, sparingly, to indicate logical sections.[link]
- Use comprehensions when possible over writing multiline for
loops.[link]
measurements = [1, 1, 2, 3, 4, 5] high_measurements = [] # No for m in measurements: if m > 3: high_measurements.append(m) # Yes high_measurements2 = [ m for m in measurements if m > 3 ] # Yes, returns generator high_measurements_gen = ( m for m in measurements if m > 3 ) # Yes, returns distinct elements (set) high_measurements_set_gen = { m for m in measurements if m > 3 } # Yes, for dictionaries employees_count = { key: value for key, value in [('Berlin', 350), ('Zurich', 50)] }
- Files using ASCII (in Python 2) or UTF-8 (in Python 3) should not have an encoding declaration.[link]
-
Imports should usually be on separate lines and in alphabetic order if they are too many.[link]
-
Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants. [link]
-
Imports should be grouped in the following order with a blank line between each group of imports: [link]
- standard library imports
- related third party imports
- local application/library specific imports
-
Wildcard imports (
from <module> import *
) should be avoided. [link]
-
Use single-quoted strings whenever possible. [link]
-
When a string contains single quote characters, use the double quote ones to avoid backslashes in the string. It improves readability.[link]
-
For triple-quoted strings, always use double quote characters to be consistent with the docstring convention in PEP 257.[link]
Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.
When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!
-
Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes! [link]
-
Comments should be complete sentences. The first word should be capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of identifiers!).[link]
An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space.
-
Use inline comments sparingly.[link]
-
Inline comments are unnecessary and in fact distracting if they state the obvious.[link]
# Don't do this: x = x + 1 # Increment x # But sometimes, this is useful: x = x + 1 # Compensate for border
-
Write docstrings for all public modules, functions, classes, and methods.[link]
-
Docstrings are not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the
def
line.[link] -
Conventions for writing good documentation strings (a.k.a. "docstrings") are immortalized in PEP 257.[link]
-
Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.[link]
-
TODOs should include the string TODO in all caps, and optionally followed by the full name of the person who can best provide context about the problem referenced by the TODO, in parentheses.[link]
-
A colon is optional.[link]
-
A comment explaining what there is to do is required. The main purpose is to have a consistent TODO format that can be searched to find the person who can provide more details upon request.[link]
-
A TODO is not a commitment that the person referenced will fix the problem. Thus when you create a TODO, it is almost always your name that is given. [link]
# No # TODO: check. # Bad # TODO(RS): Use proper namespacing for this constant. # Bad # TODO(drumm3rz4lyfe): Use proper namespacing for this constant. # Ok, but rather mention the full name # TODO: Use proper namespacing for this constant. # Good # TODO(Ringo Starr): Use proper namespacing for this constant.
-
Use
# FIXME:
to annotate problems.[link]def truncate(sentence): # FIXME: shouldn't use a global here. return sentence[:CUT]
-
Use
# TODO:
to annotate solutions to problems. [link]def truncate(sentence): # TODO: replace CUT with a function param. return sentence[:CUT]
- Never leave commented-out code in our codebase. [link]
-
Never use the characters 'l' (lowercase letter el), 'O' (uppercase letter oh), or 'I' (uppercase letter eye) as single character variable names. [link]
-
Modules (filenames) should have short, all-lowercase names. Underscores can be used in the module name if it improves readability.[link]
-
Python packages (directories) should also have short, all-lowercase names, although the use of underscores is discouraged. [link]
-
Class names should normally use the CapWords convention. [link]
- The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.
-
Because exceptions should be classes, the class naming convention applies here. However, you should use the suffix "Error" on your exception names (if the exception actually is an error).[link]
-
Function names should be lowercase, with words separated by underscores as necessary to improve readability.[link]
-
Always use
self
for the first argument to instance methods.[link] -
Always use
cls
for the first argument to class methods.[link] -
If a function argument's name clashes with a reserved keyword, it is generally better to append a single trailing underscore rather than use an abbreviation or spelling corruption. Thus
class_
is better thanclss
. (Perhaps better is to avoid such clashes by using a synonym.) [link] -
Use one leading underscore only for non-public methods and instance variables.[link]
-
Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL. [link]
-
Public attributes should have no leading underscores. [link]
-
Do not shorten words or smash them together without a separating underscore.[link]
-
It is preferred to name functions with a verb. (Even if it means putting
get_
orfind_
in front of the function name) [link] -
Use long variable names, whole words and multiple words. [link]
-
Use .format() in Python 2 and string literals in Python 3. [link]
currency = 'USD' rank = 2 rate = 0.3510 # No message = 'Currency: %s, rank: %d, rate: %.2f' % (currency, rank, rate) # Ok message = 'Currency: {}, rank: {}, rate: {:.2f}'.format(currency, rank, rate) # Good in 2.x (Too verbose but allows easier migration to Python 3.6+) message = 'Currency: {currency}, rank: {rank}, rate: {rate:.2f}'.format( currency=currency, rank=rank, rate=rate, ) # Yes in 3.x message = f'Currency: {currency}, rank: {rank}, rate: {rate:.2f}'
-
Join a list of values together using the
join
method. [link]animals = ['cat', 'dog', 'mouse'] output = ', '.join(animals)
- Avoid using regular expressions if there's a simpler and equally accurate way of expressing your target search/transformation. [link]
- Unless your regular expression is extremely simple,
always use a multi-line string and
VERBOSE
mode when representing your regular expression. [link]
- Comparisons to singletons like
None
should always be done with is or is not, never the equality operators.[link]
-
Do not check emptiness (strings, lists, tuples) through length or other means. Use the fact that empty sequences are false.[link]
# No if len(results) == 0: print('No results found.') if len(failures) > 0: print('There were failures during processing.') # Yes if not results: print('No results found.') if failures: print('There were failures during processing.')
-
Do not rely on truthiness for checking zeroness or non-zeroness though.[link]
# No: if n % 2: print('The given number is odd') if not step_count: print('No steps taken.')
# Yes: if n % 2 == 1: print('The given number is odd') if step_count == 0: print('No steps taken.')
-
Don't compare boolean values to True or False using
==
. [link]# Yes if greeting: # No if greeting == True: # Worse if greeting is True:
- Python does not have switch statements. Instead, you'll often see
Python developers use an
if
statement with manyelif
statements. Instead of using manyelif
statements, consider using a dictionary. This alternative is often (but not always) possible. [link]# No if word == 'zero': numbers.append(0) elif word == 'one': numbers.append(1) elif word == 'two': numbers.append(2) elif word == 'three': numbers.append(3) elif word == 'four': numbers.append(4) elif word == 'five': numbers.append(5) elif word == 'six': numbers.append(6) elif word == 'seven': numbers.append(7) elif word == 'eight': numbers.append(8) elif word == 'nine': numbers.append(9) else: numbers.append(' ') # Yes word_to_digit = { 'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6, 'seven': 7, 'eight': 8, 'nine': 9, } numbers.append(word_to_digit.get(word, ' '))
-
If you ever see code that sets a variable to
True
orFalse
based on a condition. Rely on truthiness by converting the condition to abool
instead, either explicitly for the truthy case or implicitly usingnot
for the falsey case.# No if results: found_results = True else: found_results = False if not failures: success = True else: success = False # Yes found_results = bool(results) success = not failures
-
Keep in mind that sometimes no conversion is necessary. The condition here is already a boolean value. So type-casting to a
bool
would be redundant. Instead simply set the variable equal to the expression.# No if n % 2 == 1: is_odd = True else: is_odd = False # Yes is_odd = (n % 2 == 1)
-
It is fine to use iterable unpacking to compact multiple assignment statements onto one line. Do this only when the assignments are very tightly related.
word1, word2 = word1.upper(), word2.upper() x, y, z = (a1 - a2), (b1 - b2), (c1 - c2)
- Whenever you see something like
some_variable[0]
orsome_variable[2]
, treat this as an indication that you should be relying on iterable unpacking.# No do_something(things[0], things[1]) do_something(things[0], things[1:-1], things[-1]) # Yes first, second = things do_something(first, second) head, *middle, tail = things do_something(head, middle, tail)
- Whenever you see yourself using tuples and indexes to access them, define a namedtuple.
# No employee = ('Guido', 'Berlin', 61) if employee[2] > 65: print(f'Sorry {employee[1]}, it is time to rest') # Yes from collections import namedtuple Employee = namedtuple('Employee', 'name city age') employee = Employee(name='Guido', city='Berlin', age=61) if employee.age > 65: print(f'Keep going {employee.name}!')
-
When catching exceptions, mention specific exceptions whenever possible instead of using a bare
except:
clause.[link] -
For all try/except clauses, limit the try clause to the absolute minimum amount of code necessary. Again, this avoids masking bugs. [link]
# Yes try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value) # No try: # Too broad! return handle_value(collection[key]) except KeyError: # Will also catch KeyError raised by handle_value() return key_not_found(key)
-
Define your custom exceptions and explicitly catch them. [link]
- Be consistent in return statements. Either all return statements in a function should return an
expression, or none of them should. If any return statement returns an expression, any return
statements where no value is returned should explicitly state this as return None, and an explicit
return statement should be present at the end of the function (if reachable).
# No def foo(x): if x >= 0: return math.sqrt(x) def bar(x): if x < 0: return return math.sqrt(x) # Yes def foo(x): if x >= 0: return math.sqrt(x) else: return None def bar(x): if x < 0: return None return math.sqrt(x)
-
If you ever see
range(len(colors))
, consider whether you actually need an index. Never do this.[link]# No for i in range(len(colors)): print(colors[i])
-
Use
zip
instead of an index to loop over multiple lists at the same time. [link]# Yes for color, ratio in zip(colors, ratios): print('{}% {}'.format(ratio * 100, color))
-
If you do really need an index, use
enumerate
. [link]# Yes for number, name in enumerate(presidents, start=1): print('President {}: {}'.format(number, name))
If you're editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around all their arithmetic operators, you should too. If their comments have little boxes of hash marks around them, make your comments have little boxes of hash marks around them too.
The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you're saying rather than on how you're saying it. We present global style rules here so people know the vocabulary, but local style is also important. If code you add to a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Avoid this.
One of Guido's key insights is that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Python code. As PEP 20 says, "Readability counts".
A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important.
However, know when to be inconsistent -- sometimes style guide recommendations just aren't applicable. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask!
—PEP 8 -- Style Guide for Python Code
In particular:
- Do not break backwards compatibility just to comply with this guide!
- Do not pep8-ify code for the sake of pep8-ify-ing it. This is meta-work and often brakes
git blame
usage.