Skip to content

Instantly share code, notes, and snippets.

@nourspace
Last active November 29, 2017 15:46
Show Gist options
  • Save nourspace/e8a30fe37d5718c8db4cfb92a699b08a to your computer and use it in GitHub Desktop.
Save nourspace/e8a30fe37d5718c8db4cfb92a699b08a to your computer and use it in GitHub Desktop.
Python Guides

Python Style Guide

This is GetYourGuide's Python Style Guide.

It is based on Python PEP 8. Portions of this guide borrow heavily from:

This 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.

Table of Contents

Layout

Indentation

  • 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):
    
    # More indentation included to distinguish this from the rest.
    def long_function_name(
            var_one, var_two, var_three,
            var_four):
        print(var_one)
    
    # 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)
    
    # Further indentation required as indentation is not distinguishable.
    def long_function_name(
        var_one, var_two, var_three,
        var_four):
        print(var_one)
    # 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,
    )

Inline

  • 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()

Line Length

  • 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 in a single line.

Line Breaks

  • 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 and or 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

  • 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,)

Blank Lines

  • 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]

Comprehensions

  • 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)]
    }

Source File Encoding

  • Files using ASCII (in Python 2) or UTF-8 (in Python 3) should not have an encoding declaration.[link]

Imports

  • 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]

    1. standard library imports
    2. related third party imports
    3. local application/library specific imports
  • Wildcard imports (from <module> import *) should be avoided. [link]

String Quotes

  • 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]

Commenting

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!

Google C++ Style Guide

  • 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]

Inline Comments

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

Documentation Strings

  • 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]

TODO comments

  • 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.

TODO vs. FIXME

  • 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]

Commented-out code

  • Never leave commented-out code in our codebase. [link]

Naming Conventions

  • 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 than clss. (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_ or find_ in front of the function name) [link]

  • Use long variable names, whole words and multiple words. [link]

Strings

  • 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)

Regular Expressions

  • 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]

Conditionals

  • Comparisons to singletons like None should always be done with is or is not, never the equality operators.[link]

Truthiness

  • 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:

Long if-elif chains

  • Python does not have switch statements. Instead, you'll often see Python developers use an if statement with many elif statements. Instead of using many elif 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, ' '))

Conversion to bool

  • If you ever see code that sets a variable to True or False based on a condition. Rely on truthiness by converting the condition to a bool instead, either explicitly for the truthy case or implicitly using not 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)

Recommendations

Compacting Assignments

  • 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)

Naming Indexes

  • Whenever you see something like some_variable[0] or some_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)

Named Tuples

  • 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}!')

Exceptions

  • 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]

Return statements

  • 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)

Loops

  • 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))

Be Consistent

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.

Google C++ Style Guide

A Foolish Consistency is the Hobgoblin of Little Minds

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.

Python Good Practices

[TOC]

Lists, Tuples, Sets

enumerate

Named Tuples

Comprehensions

measurements = [1, 1, 2, 3, 4, 5]
high_measurements = []

# Bad
for m in measurements:
    if m > 3:
        high_measurements.append(m)

# Good
high_measurements2 = [
    m
    for m in measurements
    if m > 3
]

# Good + generator
high_measurements_gen = (
    m
    for m in measurements
    if m > 3
)

# Good + distinct elements (set)
high_measurements_set_gen = {
    m
    for m in measurements
    if m > 3
}

Dictionaries

  • Use dicts for faster access

Dict Comprehension

d = {k:v for k,v in [('a', 1), ('b', 2)]}

Dict Merge

d1 = {}
d2 = {}
d3 = {}

# bad
d4 = {}
for k in d1:
    d4[k] = d1[k]
for k in d2:
    d4[k] = d2[k]
for k in d3:
    d4[k] = d3[k]

# classic good way in 2.x
d4 = d1.copy()
d4.update(d2)
d4.update(d3)

# another way in 2.x [not so clear]
d4 = {k:v for d in [d1, d2, d3] for k,v in d.items()}

# good 3.x
d4 = {**d1, **d2, **d3}

Classes

Properties

  • use instead of get/set

Context managers

  • Try, except, else, finally => with

Add __repr__ to classes

  • for better debugging

__slots__

  • saves memory
  • mutable class props

Others

Iterators

  • Loop, index, get by index => for in

Custom exceptions

  • Not extensively though

Strings format

  • Use .format()
a = 'hello'
b = 'world'

# Bad
s1 = '%s %s' % (a, b)

# Fine
s2 = '{} {}'.format(a, b)

# Good 2.x
s3 = '{a} {b}'.format(a=a, b=b)

# Good 3.x
s4 = f'{a} {b}'

Yield

Good when entire lists don't matter

  • yield from can be useful
def gen():
    for i in l:
        yield i

# 3.x
def gen2():
    yield from l

lambda

  • Good when small
  • Bad when complex
  • Do not assign a lambda to variable instead create a function

Python Style Guide

Table of Contents

This Guide

This guide is based on Python PEP 8. Portions of this guide borrow heavily from:

  • Google: C++ and Python style guides.
  • AirBNB: Ruby style guide.
  • GetYourGuide: PHP style guide.

The ultimate goal is to have code that is clean, consistent, and efficient.

Layout

Maximum Line Length

  • Limit all lines to a maximum of 120 characters.

It is not about the line length rather than clean code. Long lines mean you are doing too much in a single line.

Whitespace

  • Avoid extraneous whitespace in the following situations.
  • Avoid trailing whitespace anywhere.
  • Don't use spaces around the = sign when used to indicate a keyword argument or a default parameter value.
  • Compound statements (multiple statements on the same line) are generally discouraged.
    # 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()

Indentation

  • Use 4 spaces per indentation level.

  • Continuation lines should align wrapped elements either vertically using Python's implicit line joining inside parentheses, brackets and braces, or using a hanging indent.

    # Yes:
    
    # Aligned with opening delimiter.
    foo = long_function_name(var_one, var_two,
                             var_three, var_four)
    
    # More indentation included to distinguish this from the rest.
    def long_function_name(
            var_one, var_two, var_three,
            var_four):
        print(var_one)
    
    # Hanging indents should add a level.
    foo = long_function_name(
        var_one, var_two,
        var_three, var_four)
    # No:
    
    # Arguments on first line forbidden when not using vertical alignment.
    foo = long_function_name(var_one, var_two,
        var_three, var_four)
    
    
    # Further indentation required as indentation is not distinguishable.
    def long_function_name(
        var_one, var_two, var_three,
        var_four):
        print(var_one)
    
    # good
    def create_translation(phrase_id, phrase_key, target_locale, value, user_id):
        ...
    
    # good
    def create_translation(phrase_id,
                           phrase_key,
                           target_locale,
                           value,
                           user_id,
                           do_xss_check):
        ...
    
    # good
    def create_translation(
            phrase_id,
            phrase_key,
            target_locale,
            value,
            user_id,
            do_xss_check,
    ):
        ...
  • The closing brace/bracket/parenthesis on multiline constructs may either line up under the first non-whitespace character of the last line of list, as in:

    my_list = [
        1, 2, 3,
        4, 5, 6,
        ]
    result = some_function_that_takes_arguments(
        'a', 'b', 'c',
        'd', 'e', 'f',
        )

    or it may be lined up under the first character of the line that starts the multiline construct, as in:

    my_list = [
        1, 2, 3,
        4, 5, 6,
    ]
    result = some_function_that_takes_arguments(
        'a', 'b', 'c',
        'd', 'e', 'f',
    )
  • Add line break before binary operators.

    This applies to and and or

    # 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)

Blank Lines

  • Surround top-level function and class definitions with two blank lines.
  • Method definitions inside a class are surrounded by a single blank line.
  • Use blank lines in functions, sparingly, to indicate logical sections.

Trailing commas

  • Trailing commas are usually optional, except they are mandatory when making a tuple of one element.

    # 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).

    # Yes:
    
    FILES = [
        'setup.cfg',
        'tox.ini',
    ]
    initialize(FILES,
               error=True,
               )
    
    # No:
    
    FILES = ['setup.cfg', 'tox.ini',]
    initialize(FILES, error=True,)    

Source File Encoding

Files using ASCII (in Python 2) or UTF-8 (in Python 3) should not have an encoding declaration.

Imports

  • Imports should usually be on separate lines and in alphabetic order if they are too many.
  • Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.
  • Imports should be grouped in the following order with a blank line between each group of imports:
    1. standard library imports
    2. related third party imports
    3. local application/library specific imports
  • Wildcard imports (from <module> import *) should be avoided

String Quotes

  • Use single-quoted strings whenever possible.
  • When a string contains single quote characters, use the double quote ones to avoid backslashes in the string. It improves readability.
  • For triple-quoted strings, always use double quote characters to be consistent with the docstring convention in PEP 257.

Naming Conventions

  • Never use the characters 'l' (lowercase letter el), 'O' (uppercase letter oh), or 'I' (uppercase letter eye) as single character variable names.

  • Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves readability. Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.

  • Class names should normally use the CapWords convention.

    • The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.
  • The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.

  • 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 than clss. (Perhaps better is to avoid such clashes by using a synonym.)

  • Use one leading underscore only for non-public methods and instance variables.

  • Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL.

  • Public attributes should have no leading underscores.

  • Source file names...

Commenting

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!

Google C++ Style Guide

  • 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!
  • 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!).

Inline Comments

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.
  • Inline comments are unnecessary and in fact distracting if they state the obvious.
    # Don't do this:
    x = x + 1                 # Increment x
    
    # But sometimes, this is useful:
    x = x + 1                 # Compensate for border

Documentation Strings

  • Write docstrings for all public modules, functions, classes, and methods.
  • 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.
  • Conventions for writing good documentation strings (a.k.a. "docstrings") are immortalized in PEP 257.

TODO comments

  • Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.
  • 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.
  • A colon is optional.
  • 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.
  • 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.
      # No
      # TODO: check.
    
      # Bad
      # TODO(RS): Use proper namespacing for this constant.
    
      # Bad
      # TODO(drumm3rz4lyfe): Use proper namespacing for this constant.
    
      # Ok
      # TODO: Use proper namespacing for this constant.
    
      # Good
      # TODO(Ringo Starr): Use proper namespacing for this constant.

TODO vs. FIXME

  • Use # FIXME: to annotate problems.

    def truncate(sentence):
        # FIXME: shouldn't use a global here.
        return sentence[:CUT]
  • Use # TODO: to annotate solutions to problems.

    def truncate(sentence):
        # TODO: replace CUT with a function param.
        return sentence[:CUT]

Commented-out code

  • Never leave commented-out code in our codebase.

Recommendations

  • Comparisons to singletons like None should always be done with is or is not, never the equality operators.
  • When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause
  • For all try/except clauses, limit the try clause to the absolute minimum amount of code necessary. Again, this avoids masking bugs.
    # 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)
  • 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).
    # 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)
    
    
    # No:
    
    def foo(x):
        if x >= 0:
            return math.sqrt(x)
    
    def bar(x):
        if x < 0:
            return
        return math.sqrt(x)
  • For sequences, (strings, lists, tuples), use the fact that empty sequences are false.
    # Yes:
    if not seq:
    if seq:
    
    # No:
    if len(seq):
    if not len(seq):
  • Don't compare boolean values to True or False using ==.
    # Yes:   if greeting:
    # No:    if greeting == True:
    # Worse: if greeting is True:    

Be Consistent

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.

Google C++ Style Guide

A Foolish Consistency is the Hobgoblin of Little Minds

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!

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.

PEP 8 -- Style Guide for Python Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment