PEP 8 – Style Guide for Python Code | peps.python.org (2024)

Author:
Guido van Rossum <guido at python.org>,Barry Warsaw <barry at python.org>,Alyssa Coghlan <ncoghlan at gmail.com>
Status:
Active
Type:
Process
Created:
05-Jul-2001
Post-History:
05-Jul-2001, 01-Aug-2013
Table of Contents
  • Introduction
  • A Foolish Consistency is the Hobgoblin of Little Minds
  • Code Lay-out
    • Indentation
    • Tabs or Spaces?
    • Maximum Line Length
    • Should a Line Break Before or After a Binary Operator?
    • Blank Lines
    • Source File Encoding
    • Imports
    • Module Level Dunder Names
  • String Quotes
  • Whitespace in Expressions and Statements
    • Pet Peeves
    • Other Recommendations
  • When to Use Trailing Commas
  • Comments
    • Block Comments
    • Inline Comments
    • Documentation Strings
  • Naming Conventions
    • Overriding Principle
    • Descriptive: Naming Styles
    • Prescriptive: Naming Conventions
      • Names to Avoid
      • ASCII Compatibility
      • Package and Module Names
      • Class Names
      • Type Variable Names
      • Exception Names
      • Global Variable Names
      • Function and Variable Names
      • Function and Method Arguments
      • Method Names and Instance Variables
      • Constants
      • Designing for Inheritance
    • Public and Internal Interfaces
  • Programming Recommendations
    • Function Annotations
    • Variable Annotations
  • References
  • Copyright

Introduction

This document gives coding conventions for the Python code comprisingthe standard library in the main Python distribution. Please see thecompanion informational PEP describing style guidelines for the C codein the C implementation of Python.

This document and PEP 257 (Docstring Conventions) were adapted fromGuido’s original Python Style Guide essay, with some additions fromBarry’s style guide [2].

This style guide evolves over time as additional conventions areidentified and past conventions are rendered obsolete by changes inthe language itself.

Many projects have their own coding style guidelines. In the event of anyconflicts, such project-specific guides take precedence for that project.

A Foolish Consistency is the Hobgoblin of Little Minds

One of Guido’s key insights is that code is read much more often thanit is written. The guidelines provided here are intended to improvethe readability of code and make it consistent across the widespectrum of Python code. As PEP 20 says, “Readability counts”.

A style guide is about consistency. Consistency with this style guideis 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 guiderecommendations just aren’t applicable. When in doubt, use your bestjudgment. Look at other examples and decide what looks best. Anddon’t hesitate to ask!

In particular: do not break backwards compatibility just to comply withthis PEP!

Some other good reasons to ignore a particular guideline:

  1. When applying the guideline would make the code less readable, evenfor someone who is used to reading code that follows this PEP.
  2. To be consistent with surrounding code that also breaks it (maybefor historic reasons) – although this is also an opportunity toclean up someone else’s mess (in true XP style).
  3. Because the code in question predates the introduction of theguideline and there is no other reason to be modifying that code.
  4. When the code needs to remain compatible with older versions ofPython that don’t support the feature recommended by the style guide.

Code Lay-out

Indentation

Use 4 spaces per indentation level.

Continuation lines should align wrapped elements either verticallyusing Python’s implicit line joining inside parentheses, brackets andbraces, or using a hanging indent [1]. When using a hangingindent the following should be considered; there should be noarguments on the first line and further indentation should be used toclearly distinguish itself as a continuation line:

# Correct:# Aligned with opening delimiter.foo = long_function_name(var_one, var_two, var_three, var_four)# Add 4 spaces (an extra level of indentation) to distinguish arguments 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)
# Wrong:# 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)

The 4-space rule is optional for continuation lines.

Optional:

# Hanging indents *may* be indented to other than 4 spaces.foo = long_function_name( var_one, var_two, var_three, var_four)

When the conditional part of an if-statement is long enough to requirethat it be written across multiple lines, it’s worth noting that thecombination of a two character keyword (i.e. if), plus a single space,plus an opening parenthesis creates a natural 4-space indent for thesubsequent lines of the multiline conditional. This can produce a visualconflict with the indented suite of code nested inside the if-statement,which would also naturally be indented to 4 spaces. This PEP takes noexplicit position on how (or whether) to further visually distinguish suchconditional lines from the nested suite inside the if-statement.Acceptable options in this situation include, but are not limited to:

# No extra indentation.if (this_is_one_thing and that_is_another_thing): do_something()# Add a comment, which will provide some distinction in editors# supporting syntax highlighting.if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something()# Add some extra indentation on the conditional continuation line.if (this_is_one_thing and that_is_another_thing): do_something()

(Also see the discussion of whether to break before or after binaryoperators below.)

The closing brace/bracket/parenthesis on multiline constructs mayeither line up under the first non-whitespace character of the lastline 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 thatstarts 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',)

Tabs or Spaces?

Spaces are the preferred indentation method.

Tabs should be used solely to remain consistent with code that isalready indented with tabs.

Python disallows mixing tabs and spaces for indentation.

Maximum Line Length

Limit all lines to a maximum of 79 characters.

For flowing long blocks of text with fewer structural restrictions(docstrings or comments), the line length should be limited to 72characters.

Limiting the required editor window width makes it possible to haveseveral files open side by side, and works well when using codereview tools that present the two versions in adjacent columns.

The default wrapping in most tools disrupts the visual structure of thecode, making it more difficult to understand. The limits are chosen toavoid wrapping in editors with the window width set to 80, evenif the tool places a marker glyph in the final column when wrappinglines. Some web based tools may not offer dynamic line wrapping at all.

Some teams strongly prefer a longer line length. For code maintainedexclusively or primarily by a team that can reach agreement on thisissue, it is okay to increase the line length limit up to 99 characters,provided that comments and docstrings are still wrapped at 72characters.

The Python standard library is conservative and requires limitinglines to 79 characters (and docstrings/comments to 72).

The preferred way of wrapping long lines is by using Python’s impliedline continuation inside parentheses, brackets and braces. Long linescan be broken over multiple lines by wrapping expressions inparentheses. These should be used in preference to using a backslashfor line continuation.

Backslashes may still be appropriate at times. For example, long,multiple with-statements could not use implicit continuationbefore Python 3.10, so backslashes were acceptable for that case:

with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2: file_2.write(file_1.read())

(See the previous discussion on multiline if-statements for furtherthoughts on the indentation of such multiline with-statements.)

Another such case is with assert statements.

Make sure to indent the continued line appropriately.

Should a Line Break Before or After a Binary Operator?

For decades the recommended style was to break after binary operators.But this can hurt readability in two ways: the operators tend to getscattered across different columns on the screen, and each operator ismoved away from its operand and onto the previous line. Here, the eyehas to do extra work to tell which items are added and which aresubtracted:

# Wrong:# operators sit far away from their operandsincome = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)

To solve this readability problem, mathematicians and their publishersfollow the opposite convention. Donald Knuth explains the traditionalrule in his Computers and Typesetting series: “Although formulaswithin a paragraph always break after binary operations and relations,displayed formulas always break before binary operations” [3].

Following the tradition from mathematics usually results in morereadable code:

# Correct:# easy to match operators with operandsincome = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)

In Python code, it is permissible to break before or after a binaryoperator, as long as the convention is consistent locally. For newcode Knuth’s style is suggested.

Blank Lines

Surround top-level function and class definitions with two blanklines.

Method definitions inside a class are surrounded by a single blankline.

Extra blank lines may be used (sparingly) to separate groups ofrelated functions. Blank lines may be omitted between a bunch ofrelated one-liners (e.g. a set of dummy implementations).

Use blank lines in functions, sparingly, to indicate logical sections.

Python accepts the control-L (i.e. ^L) form feed character aswhitespace; many tools treat these characters as page separators, soyou may use them to separate pages of related sections of your file.Note, some editors and web-based code viewers may not recognizecontrol-L as a form feed and will show another glyph in its place.

Source File Encoding

Code in the core Python distribution should always use UTF-8, and should nothave an encoding declaration.

In the standard library, non-UTF-8 encodings should be used only fortest purposes. Use non-ASCII characters sparingly, preferably only todenote places and human names. If using non-ASCII characters as data,avoid noisy Unicode characters like z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘ and byte ordermarks.

All identifiers in the Python standard library MUST use ASCII-onlyidentifiers, and SHOULD use English words wherever feasible (in manycases, abbreviations and technical terms are used which aren’tEnglish).

Open source projects with a global audience are encouraged to adopt asimilar policy.

Imports

  • Imports should usually be on separate lines:
    # Correct:import osimport sys
    # Wrong:import sys, os

    It’s okay to say this though:

    # Correct:from subprocess import Popen, PIPE
  • Imports are always put at the top of the file, just after any modulecomments and docstrings, and before module globals and constants.

    Imports should be grouped in the following order:

    1. Standard library imports.
    2. Related third party imports.
    3. Local application/library specific imports.

    You should put a blank line between each group of imports.

  • Absolute imports are recommended, as they are usually more readableand tend to be better behaved (or at least give better errormessages) if the import system is incorrectly configured (such aswhen a directory inside a package ends up on sys.path):
    import mypkg.siblingfrom mypkg import siblingfrom mypkg.sibling import example

    However, explicit relative imports are an acceptable alternative toabsolute imports, especially when dealing with complex package layoutswhere using absolute imports would be unnecessarily verbose:

    from . import siblingfrom .sibling import example

    Standard library code should avoid complex package layouts and alwaysuse absolute imports.

  • When importing a class from a class-containing module, it’s usuallyokay to spell this:
    from myclass import MyClassfrom foo.bar.yourclass import YourClass

    If this spelling causes local name clashes, then spell them explicitly:

    import myclassimport foo.bar.yourclass

    and use myclass.MyClass and foo.bar.yourclass.YourClass.

  • Wildcard imports (from <module> import *) should be avoided, asthey make it unclear which names are present in the namespace,confusing both readers and many automated tools. There is onedefensible use case for a wildcard import, which is to republish aninternal interface as part of a public API (for example, overwritinga pure Python implementation of an interface with the definitionsfrom an optional accelerator module and exactly which definitionswill be overwritten isn’t known in advance).

    When republishing names this way, the guidelines below regardingpublic and internal interfaces still apply.

Module Level Dunder Names

Module level “dunders” (i.e. names with two leading and two trailingunderscores) such as __all__, __author__, __version__,etc. should be placed after the module docstring but before any importstatements except from __future__ imports. Python mandates thatfuture-imports must appear in the module before any other code exceptdocstrings:

"""This is the example module.This module does stuff."""from __future__ import barry_as_FLUFL__all__ = ['a', 'b', 'c']__version__ = '0.1'__author__ = 'Cardinal Biggles'import osimport sys

String Quotes

In Python, single-quoted strings and double-quoted strings are thesame. This PEP does not make a recommendation for this. Pick a ruleand stick to it. When a string contains single or double quotecharacters, however, use the other one to avoid backslashes in thestring. It improves readability.

For triple-quoted strings, always use double quote characters to beconsistent with the docstring convention in PEP 257.

Whitespace in Expressions and Statements

Pet Peeves

Avoid extraneous whitespace in the following situations:

  • Immediately inside parentheses, brackets or braces:
    # Correct:spam(ham[1], {eggs: 2})
    # Wrong:spam( ham[ 1 ], { eggs: 2 } )
  • Between a trailing comma and a following close parenthesis:
    # Correct:foo = (0,)
    # Wrong:bar = (0, )
  • Immediately before a comma, semicolon, or colon:
    # Correct:if x == 4: print(x, y); x, y = y, x
    # Wrong:if x == 4 : print(x , y) ; x , y = y , x
  • However, in a slice the colon acts like a binary operator, andshould have equal amounts on either side (treating it as theoperator with the lowest priority). In an extended slice, bothcolons must have the same amount of spacing applied. Exception:when a slice parameter is omitted, the space is omitted:
    # Correct:ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]ham[lower:upper], ham[lower:upper:], ham[lower::step]ham[lower+offset : upper+offset]ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]ham[lower + offset : upper + offset]
    # Wrong:ham[lower + offset:upper + offset]ham[1: 9], ham[1 :9], ham[1:9 :3]ham[lower : : step]ham[ : upper]
  • Immediately before the open parenthesis that starts the argumentlist of a function call:
    # Correct:spam(1)
    # Wrong:spam (1)
  • Immediately before the open parenthesis that starts an indexing orslicing:
    # Correct:dct['key'] = lst[index]
    # Wrong:dct ['key'] = lst [index]
  • More than one space around an assignment (or other) operator toalign it with another:
    # Correct:x = 1y = 2long_variable = 3
    # Wrong:x = 1y = 2long_variable = 3

Other Recommendations

  • Avoid trailing whitespace anywhere. Because it’s usually invisible,it can be confusing: e.g. a backslash followed by a space and anewline does not count as a line continuation marker. Some editorsdon’t preserve it and many projects (like CPython itself) havepre-commit hooks that reject it.
  • Always surround these binary operators with a single space on eitherside: assignment (=), augmented assignment (+=, -=etc.), comparisons (==, <, >, !=, <>, <=,>=, in, not in, is, is not), Booleans (and,or, not).
  • If operators with different priorities are used, consider addingwhitespace around the operators with the lowest priority(ies). Useyour own judgment; however, never use more than one space, andalways have the same amount of whitespace on both sides of a binaryoperator:
    # Correct:i = i + 1submitted += 1x = x*2 - 1hypot2 = x*x + y*yc = (a+b) * (a-b)
    # Wrong:i=i+1submitted +=1x = x * 2 - 1hypot2 = x * x + y * yc = (a + b) * (a - b)
  • Function annotations should use the normal rules for colons andalways have spaces around the -> arrow if present. (SeeFunction Annotations below for more about function annotations.):
    # Correct:def munge(input: AnyStr): ...def munge() -> PosInt: ...
    # Wrong:def munge(input:AnyStr): ...def munge()->PosInt: ...
  • Don’t use spaces around the = sign when used to indicate akeyword argument, or when used to indicate a default value for anunannotated function parameter:
    # Correct:def complex(real, imag=0.0): return magic(r=real, i=imag)
    # Wrong:def complex(real, imag = 0.0): return magic(r = real, i = imag)

    When combining an argument annotation with a default value, however, do usespaces around the = sign:

    # Correct:def munge(sep: AnyStr = None): ...def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
    # Wrong:def munge(input: AnyStr=None): ...def munge(input: AnyStr, limit = 1000): ...
  • Compound statements (multiple statements on the same line) aregenerally discouraged:
    # Correct:if foo == 'blah': do_blah_thing()do_one()do_two()do_three()

    Rather not:

    # Wrong:if foo == 'blah': do_blah_thing()do_one(); do_two(); do_three()
  • While sometimes it’s okay to put an if/for/while with a small bodyon the same line, never do this for multi-clause statements. Alsoavoid folding such long lines!

    Rather not:

    # Wrong:if foo == 'blah': do_blah_thing()for x in lst: total += xwhile t < 10: t = delay()

    Definitely not:

    # Wrong: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()

When to Use Trailing Commas

Trailing commas are usually optional, except they are mandatory whenmaking a tuple of one element. For clarity, it is recommended tosurround the latter in (technically redundant) parentheses:

# Correct:FILES = ('setup.cfg',)
# Wrong:FILES = 'setup.cfg',

When trailing commas are redundant, they are often helpful when aversion control system is used, when a list of values, arguments orimported items is expected to be extended over time. The pattern isto put each value (etc.) on a line by itself, always adding a trailingcomma, and add the close parenthesis/bracket/brace on the next line.However it does not make sense to have a trailing comma on the sameline as the closing delimiter (except in the above case of singletontuples):

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

Comments

Comments that contradict the code are worse than no comments. Alwaysmake a priority of keeping the comments up-to-date when the codechanges!

Comments should be complete sentences. The first word should becapitalized, unless it is an identifier that begins with a lower caseletter (never alter the case of identifiers!).

Block comments generally consist of one or more paragraphs built out ofcomplete sentences, with each sentence ending in a period.

You should use one or two spaces after a sentence-ending period inmulti-sentence comments, except after the final sentence.

Ensure that your comments are clear and easily understandable to otherspeakers of the language you are writing in.

Python coders from non-English speaking countries: please write yourcomments in English, unless you are 120% sure that the code will neverbe read by people who don’t speak your language.

Block Comments

Block comments generally apply to some (or all) code that followsthem, and are indented to the same level as that code. Each line of ablock comment starts with a # and a single space (unless it isindented text inside the comment).

Paragraphs inside a block comment are separated by a line containing asingle #.

Inline Comments

Use inline comments sparingly.

An inline comment is a comment on the same line as a statement.Inline comments should be separated by at least two spaces from thestatement. They should start with a # and a single space.

Inline comments are unnecessary and in fact distracting if they statethe obvious. Don’t do this:

x = x + 1 # Increment x

But sometimes, this is useful:

x = x + 1 # Compensate for border

Documentation Strings

Conventions for writing good documentation strings(a.k.a. “docstrings”) are immortalized in PEP 257.

  • Write docstrings for all public modules, functions, classes, andmethods. Docstrings are not necessary for non-public methods, butyou should have a comment that describes what the method does. Thiscomment should appear after the def line.
  • PEP 257 describes good docstring conventions. Note that mostimportantly, the """ that ends a multiline docstring should beon a line by itself:
    """Return a foobangOptional plotz says to frobnicate the bizbaz first."""
  • For one liner docstrings, please keep the closing """ onthe same line:
    """Return an ex-parrot."""

Naming Conventions

The naming conventions of Python’s library are a bit of a mess, sowe’ll never get this completely consistent – nevertheless, here arethe currently recommended naming standards. New modules and packages(including third party frameworks) should be written to thesestandards, but where an existing library has a different style,internal consistency is preferred.

Overriding Principle

Names that are visible to the user as public parts of the API shouldfollow conventions that reflect usage rather than implementation.

Descriptive: Naming Styles

There are a lot of different naming styles. It helps to be able torecognize what naming style is being used, independently from whatthey are used for.

The following naming styles are commonly distinguished:

  • b (single lowercase letter)
  • B (single uppercase letter)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords (or CapWords, or CamelCase – so named becauseof the bumpy look of its letters [4]). This is also sometimes knownas StudlyCaps.

    Note: When using acronyms in CapWords, capitalize all theletters of the acronym. Thus HTTPServerError is better thanHttpServerError.

  • mixedCase (differs from CapitalizedWords by initial lowercasecharacter!)
  • Capitalized_Words_With_Underscores (ugly!)

There’s also the style of using a short unique prefix to group relatednames together. This is not used much in Python, but it is mentionedfor completeness. For example, the os.stat() function returns atuple whose items traditionally have names like st_mode,st_size, st_mtime and so on. (This is done to emphasize thecorrespondence with the fields of the POSIX system call struct, whichhelps programmers familiar with that.)

The X11 library uses a leading X for all its public functions. InPython, this style is generally deemed unnecessary because attributeand method names are prefixed with an object, and function names areprefixed with a module name.

In addition, the following special forms using leading or trailingunderscores are recognized (these can generally be combined with anycase convention):

  • _single_leading_underscore: weak “internal use” indicator.E.g. from M import * does not import objects whose names startwith an underscore.
  • single_trailing_underscore_: used by convention to avoidconflicts with Python keyword, e.g. :
    tkinter.Toplevel(master, class_='ClassName')
  • __double_leading_underscore: when naming a class attribute,invokes name mangling (inside class FooBar, __boo becomes_FooBar__boo; see below).
  • __double_leading_and_trailing_underscore__: “magic” objects orattributes that live in user-controlled namespaces.E.g. __init__, __import__ or __file__. Never inventsuch names; only use them as documented.

Prescriptive: Naming Conventions

Names to Avoid

Never use the characters ‘l’ (lowercase letter el), ‘O’ (uppercaseletter oh), or ‘I’ (uppercase letter eye) as single character variablenames.

In some fonts, these characters are indistinguishable from thenumerals one and zero. When tempted to use ‘l’, use ‘L’ instead.

ASCII Compatibility

Identifiers used in the standard library must be ASCII compatibleas described in thepolicy sectionof PEP 3131.

Package and Module Names

Modules should have short, all-lowercase names. Underscores can beused in the module name if it improves readability. Python packagesshould also have short, all-lowercase names, although the use ofunderscores is discouraged.

When an extension module written in C or C++ has an accompanyingPython module that provides a higher level (e.g. more object oriented)interface, the C/C++ module has a leading underscore(e.g. _socket).

Class Names

Class names should normally use the CapWords convention.

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

Note that there is a separate convention for builtin names: most builtinnames are single words (or two words run together), with the CapWordsconvention used only for exception names and builtin constants.

Type Variable Names

Names of type variables introduced in PEP 484 should normally use CapWordspreferring short names: T, AnyStr, Num. It is recommended to addsuffixes _co or _contra to the variables used to declare covariantor contravariant behavior correspondingly:

from typing import TypeVarVT_co = TypeVar('VT_co', covariant=True)KT_contra = TypeVar('KT_contra', contravariant=True)

Exception Names

Because exceptions should be classes, the class naming conventionapplies here. However, you should use the suffix “Error” on yourexception names (if the exception actually is an error).

Global Variable Names

(Let’s hope that these variables are meant for use inside one moduleonly.) The conventions are about the same as those for functions.

Modules that are designed for use via from M import * should usethe __all__ mechanism to prevent exporting globals, or use theolder convention of prefixing such globals with an underscore (whichyou might want to do to indicate these globals are “modulenon-public”).

Function and Variable Names

Function names should be lowercase, with words separated byunderscores as necessary to improve readability.

Variable names follow the same convention as function names.

mixedCase is allowed only in contexts where that’s already theprevailing style (e.g. threading.py), to retain backwardscompatibility.

Function and Method Arguments

Always use self for the first argument to instance methods.

Always use cls for the first argument to class methods.

If a function argument’s name clashes with a reserved keyword, it isgenerally better to append a single trailing underscore rather thanuse an abbreviation or spelling corruption. Thus class_ is betterthan clss. (Perhaps better is to avoid such clashes by using asynonym.)

Method Names and Instance Variables

Use the function naming rules: lowercase with words separated byunderscores as necessary to improve readability.

Use one leading underscore only for non-public methods and instancevariables.

To avoid name clashes with subclasses, use two leading underscores toinvoke Python’s name mangling rules.

Python mangles these names with the class name: if class Foo has anattribute named __a, it cannot be accessed by Foo.__a. (Aninsistent user could still gain access by calling Foo._Foo__a.)Generally, double leading underscores should be used only to avoidname conflicts with attributes in classes designed to be subclassed.

Note: there is some controversy about the use of __names (see below).

Constants

Constants are usually defined on a module level and written in allcapital letters with underscores separating words. Examples includeMAX_OVERFLOW and TOTAL.

Designing for Inheritance

Always decide whether a class’s methods and instance variables(collectively: “attributes”) should be public or non-public. If indoubt, choose non-public; it’s easier to make it public later than tomake a public attribute non-public.

Public attributes are those that you expect unrelated clients of yourclass to use, with your commitment to avoid backwards incompatiblechanges. Non-public attributes are those that are not intended to beused by third parties; you make no guarantees that non-publicattributes won’t change or even be removed.

We don’t use the term “private” here, since no attribute is reallyprivate in Python (without a generally unnecessary amount of work).

Another category of attributes are those that are part of the“subclass API” (often called “protected” in other languages). Someclasses are designed to be inherited from, either to extend or modifyaspects of the class’s behavior. When designing such a class, takecare to make explicit decisions about which attributes are public,which are part of the subclass API, and which are truly only to beused by your base class.

With this in mind, here are the Pythonic guidelines:

  • Public attributes should have no leading underscores.
  • If your public attribute name collides with a reserved keyword,append a single trailing underscore to your attribute name. This ispreferable to an abbreviation or corrupted spelling. (However,notwithstanding this rule, ‘cls’ is the preferred spelling for anyvariable or argument which is known to be a class, especially thefirst argument to a class method.)

    Note 1: See the argument name recommendation above for class methods.

  • For simple public data attributes, it is best to expose just theattribute name, without complicated accessor/mutator methods. Keepin mind that Python provides an easy path to future enhancement,should you find that a simple data attribute needs to growfunctional behavior. In that case, use properties to hidefunctional implementation behind simple data attribute accesssyntax.

    Note 1: Try to keep the functional behavior side-effect free,although side-effects such as caching are generally fine.

    Note 2: Avoid using properties for computationally expensiveoperations; the attribute notation makes the caller believe thataccess is (relatively) cheap.

  • If your class is intended to be subclassed, and you have attributesthat you do not want subclasses to use, consider naming them withdouble leading underscores and no trailing underscores. Thisinvokes Python’s name mangling algorithm, where the name of theclass is mangled into the attribute name. This helps avoidattribute name collisions should subclasses inadvertently containattributes with the same name.

    Note 1: Note that only the simple class name is used in the mangledname, so if a subclass chooses both the same class name and attributename, you can still get name collisions.

    Note 2: Name mangling can make certain uses, such as debugging and__getattr__(), less convenient. However the name manglingalgorithm is well documented and easy to perform manually.

    Note 3: Not everyone likes name mangling. Try to balance theneed to avoid accidental name clashes with potential use byadvanced callers.

Public and Internal Interfaces

Any backwards compatibility guarantees apply only to public interfaces.Accordingly, it is important that users be able to clearly distinguishbetween public and internal interfaces.

Documented interfaces are considered public, unless the documentationexplicitly declares them to be provisional or internal interfaces exemptfrom the usual backwards compatibility guarantees. All undocumentedinterfaces should be assumed to be internal.

To better support introspection, modules should explicitly declare thenames in their public API using the __all__ attribute. Setting__all__ to an empty list indicates that the module has no public API.

Even with __all__ set appropriately, internal interfaces (packages,modules, classes, functions, attributes or other names) should still beprefixed with a single leading underscore.

An interface is also considered internal if any containing namespace(package, module or class) is considered internal.

Imported names should always be considered an implementation detail.Other modules must not rely on indirect access to such imported namesunless they are an explicitly documented part of the containing module’sAPI, such as os.path or a package’s __init__ module that exposesfunctionality from submodules.

Programming Recommendations

  • Code should be written in a way that does not disadvantage otherimplementations of Python (PyPy, Jython, IronPython, Cython, Psyco,and such).

    For example, do not rely on CPython’s efficient implementation ofin-place string concatenation for statements in the form a += bor a = a + b. This optimization is fragile even in CPython (itonly works for some types) and isn’t present at all in implementationsthat don’t use refcounting. In performance sensitive parts of thelibrary, the ''.join() form should be used instead. This willensure that concatenation occurs in linear time across variousimplementations.

  • Comparisons to singletons like None should always be done withis or is not, never the equality operators.

    Also, beware of writing if x when you really mean if x is notNone – e.g. when testing whether a variable or argument thatdefaults to None was set to some other value. The other value mighthave a type (such as a container) that could be false in a booleancontext!

  • Use is not operator rather than not ... is. While bothexpressions are functionally identical, the former is more readableand preferred:
    # Correct:if foo is not None:
    # Wrong:if not foo is None:
  • When implementing ordering operations with rich comparisons, it isbest to implement all six operations (__eq__, __ne__,__lt__, __le__, __gt__, __ge__) rather than relyingon other code to only exercise a particular comparison.

    To minimize the effort involved, the functools.total_ordering()decorator provides a tool to generate missing comparison methods.

    PEP 207 indicates that reflexivity rules are assumed by Python.Thus, the interpreter may swap y > x with x < y, y >= xwith x <= y, and may swap the arguments of x == y and x !=y. The sort() and min() operations are guaranteed to usethe < operator and the max() function uses the >operator. However, it is best to implement all six operations sothat confusion doesn’t arise in other contexts.

  • Always use a def statement instead of an assignment statement that bindsa lambda expression directly to an identifier:
    # Correct:def f(x): return 2*x
    # Wrong:f = lambda x: 2*x

    The first form means that the name of the resulting function object isspecifically ‘f’ instead of the generic ‘<lambda>’. This is moreuseful for tracebacks and string representations in general. The useof the assignment statement eliminates the sole benefit a lambdaexpression can offer over an explicit def statement (i.e. that it canbe embedded inside a larger expression)

  • Derive exceptions from Exception rather than BaseException.Direct inheritance from BaseException is reserved for exceptionswhere catching them is almost always the wrong thing to do.

    Design exception hierarchies based on the distinctions that codecatching the exceptions is likely to need, rather than the locationswhere the exceptions are raised. Aim to answer the question“What went wrong?” programmatically, rather than only stating that“A problem occurred” (see PEP 3151 for an example of this lesson beinglearned for the builtin exception hierarchy)

    Class naming conventions apply here, although you should add thesuffix “Error” to your exception classes if the exception is anerror. Non-error exceptions that are used for non-local flow controlor other forms of signaling need no special suffix.

  • Use exception chaining appropriately. raise X from Yshould be used to indicate explicit replacement without losing theoriginal traceback.

    When deliberately replacing an inner exception (using raise X fromNone), ensure that relevant details are transferred to the newexception (such as preserving the attribute name when convertingKeyError to AttributeError, or embedding the text of the originalexception in the new exception message).

  • When catching exceptions, mention specific exceptions wheneverpossible instead of using a bare except: clause:
    try: import platform_specific_moduleexcept ImportError: platform_specific_module = None

    A bare except: clause will catch SystemExit andKeyboardInterrupt exceptions, making it harder to interrupt aprogram with Control-C, and can disguise other problems. If youwant to catch all exceptions that signal program errors, useexcept Exception: (bare except is equivalent to exceptBaseException:).

    A good rule of thumb is to limit use of bare ‘except’ clauses to twocases:

    1. If the exception handler will be printing out or logging thetraceback; at least the user will be aware that an error hasoccurred.
    2. If the code needs to do some cleanup work, but then lets theexception propagate upwards with raise. try...finallycan be a better way to handle this case.
  • When catching operating system errors, prefer the explicit exceptionhierarchy introduced in Python 3.3 over introspection of errnovalues.
  • Additionally, for all try/except clauses, limit the try clauseto the absolute minimum amount of code necessary. Again, thisavoids masking bugs:
    # Correct:try: value = collection[key]except KeyError: return key_not_found(key)else: return handle_value(value)
    # Wrong:try: # Too broad! return handle_value(collection[key])except KeyError: # Will also catch KeyError raised by handle_value() return key_not_found(key)
  • When a resource is local to a particular section of code, use awith statement to ensure it is cleaned up promptly and reliablyafter use. A try/finally statement is also acceptable.
  • Context managers should be invoked through separate functions or methodswhenever they do something other than acquire and release resources:
    # Correct:with conn.begin_transaction(): do_stuff_in_transaction(conn)
    # Wrong:with conn: do_stuff_in_transaction(conn)

    The latter example doesn’t provide any information to indicate thatthe __enter__ and __exit__ methods are doing something otherthan closing the connection after a transaction. Being explicit isimportant in this case.

  • Be consistent in return statements. Either all return statements ina function should return an expression, or none of them should. Ifany return statement returns an expression, any return statementswhere no value is returned should explicitly state this as returnNone, and an explicit return statement should be present at theend of the function (if reachable):
    # Correct:def foo(x): if x >= 0: return math.sqrt(x) else: return Nonedef bar(x): if x < 0: return None return math.sqrt(x)
    # Wrong:def foo(x): if x >= 0: return math.sqrt(x)def bar(x): if x < 0: return return math.sqrt(x)
  • Use ''.startswith() and ''.endswith() instead of stringslicing to check for prefixes or suffixes.

    startswith() and endswith() are cleaner and less error prone:

    # Correct:if foo.startswith('bar'):
    # Wrong:if foo[:3] == 'bar':
  • Object type comparisons should always use isinstance() instead ofcomparing types directly:
    # Correct:if isinstance(obj, int):
    # Wrong:if type(obj) is type(1):
  • For sequences, (strings, lists, tuples), use the fact that emptysequences are false:
    # Correct:if not seq:if seq:
    # Wrong:if len(seq):if not len(seq):
  • Don’t write string literals that rely on significant trailingwhitespace. Such trailing whitespace is visually indistinguishableand some editors (or more recently, reindent.py) will trim them.
  • Don’t compare boolean values to True or False using ==:
    # Correct:if greeting:
    # Wrong:if greeting == True:

    Worse:

    # Wrong:if greeting is True:
  • Use of the flow control statements return/break/continuewithin the finally suite of a try...finally, where the flow controlstatement would jump outside the finally suite, is discouraged. Thisis because such statements will implicitly cancel any active exceptionthat is propagating through the finally suite:
    # Wrong:def foo(): try: 1 / 0 finally: return 42

Function Annotations

With the acceptance of PEP 484, the style rules for functionannotations have changed.

  • Function annotations should use PEP 484 syntax (there are someformatting recommendations for annotations in the previous section).
  • The experimentation with annotation styles that was recommendedpreviously in this PEP is no longer encouraged.
  • However, outside the stdlib, experiments within the rules of PEP 484are now encouraged. For example, marking up a large third partylibrary or application with PEP 484 style type annotations,reviewing how easy it was to add those annotations, and observingwhether their presence increases code understandability.
  • The Python standard library should be conservative in adopting suchannotations, but their use is allowed for new code and for bigrefactorings.
  • For code that wants to make a different use of function annotationsit is recommended to put a comment of the form:
    # type: ignore

    near the top of the file; this tells type checkers to ignore allannotations. (More fine-grained ways of disabling complaints fromtype checkers can be found in PEP 484.)

  • Like linters, type checkers are optional, separate tools. Pythoninterpreters by default should not issue any messages due to typechecking and should not alter their behavior based on annotations.
  • Users who don’t want to use type checkers are free to ignore them.However, it is expected that users of third party library packagesmay want to run type checkers over those packages. For this purposePEP 484 recommends the use of stub files: .pyi files that are readby the type checker in preference of the corresponding .py files.Stub files can be distributed with a library, or separately (withthe library author’s permission) through the typeshed repo [5].

Variable Annotations

PEP 526 introduced variable annotations. The style recommendations for them aresimilar to those on function annotations described above:

  • Annotations for module level variables, class and instance variables,and local variables should have a single space after the colon.
  • There should be no space before the colon.
  • If an assignment has a right hand side, then the equality sign should haveexactly one space on both sides:
    # Correct:code: intclass Point: coords: Tuple[int, int] label: str = '<unknown>'
    # Wrong:code:int # No space after coloncode : int # Space before colonclass Test: result: int=0 # No spaces around equality sign
  • Although the PEP 526 is accepted for Python 3.6, the variable annotationsyntax is the preferred syntax for stub files on all versions of Python(see PEP 484 for details).

Footnotes

References

Copyright

This document has been placed in the public domain.

PEP 8 – Style Guide for Python Code | peps.python.org (2024)

References

Top Articles
Latest Posts
Article information

Author: Dr. Pierre Goyette

Last Updated:

Views: 6362

Rating: 5 / 5 (70 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Dr. Pierre Goyette

Birthday: 1998-01-29

Address: Apt. 611 3357 Yong Plain, West Audra, IL 70053

Phone: +5819954278378

Job: Construction Director

Hobby: Embroidery, Creative writing, Shopping, Driving, Stand-up comedy, Coffee roasting, Scrapbooking

Introduction: My name is Dr. Pierre Goyette, I am a enchanting, powerful, jolly, rich, graceful, colorful, zany person who loves writing and wants to share my knowledge and understanding with you.