Skip to content

Instantly share code, notes, and snippets.

@nafisk
Last active August 12, 2024 21:18
Show Gist options
  • Save nafisk/8594e7c0eb4d97aea9e17447dbee7b40 to your computer and use it in GitHub Desktop.
Save nafisk/8594e7c0eb4d97aea9e17447dbee7b40 to your computer and use it in GitHub Desktop.
PEP8 Style Guide Summary

PEP 8 – Style Guide for Python Code

πŸ“ Code Layout

  • Indentation: Use 4 spaces per indentation level. Continuation lines should align with the opening delimiter or use a hanging indent. Hanging indents should not have arguments on the first line and should further indent to distinguish from the rest.

    # Correct:
    def long_function_name(var_one, var_two,
                           var_three, var_four):
        print(var_one)
    
    # Correct (hanging indent):
    def long_function_name(
            var_one, var_two, var_three,
            var_four):
        print(var_one)
    
    # Wrong:
    def long_function_name(var_one, var_two,
        var_three, var_four):
        print(var_one)
    
  • Tabs or Spaces: Spaces are preferred over tabs for indentation. Mixing tabs and spaces is disallowed.

    # Correct:
    if True:
        print("Spaces for indentation")
    
    # Wrong:
    if True:
    β†’   print("Tabs for indentation")
    
  • Maximum Line Length: Limit lines to 79 characters, and 72 characters for comments and docstrings. For teams preferring longer lines, the limit can be extended to 99 characters, provided comments and docstrings are still wrapped at 72 characters.

    # Correct:
    comment = "This is an example within the length limit."
    
    # Wrong:
    long_comment = "This line exceeds the recommended maximum length, which is discouraged."
    
  • Blank Lines: Surround top-level function and class definitions with two blank lines. Use a single blank line to separate method definitions inside a class. Extra blank lines can be used sparingly to separate related code sections.

    # Correct:
    
    class MyClass:
        
        def method(self):
            pass
    
    # Wrong:
    class MyClass:
        def method(self):
            pass
    
  • Source File Encoding: Always use UTF-8 encoding. Non-ASCII characters should be used sparingly, preferably only for human names or specific data.

    # Correct:
    # -*- coding: utf-8 -*-
    
    author = "JosΓ©"
    
    # Wrong:
    author = "Jos\351"  # Non-UTF-8 encoding
    

πŸ“¦ Imports

  • Import Format: Imports should be on separate lines. Group imports in the following order: standard library imports, third-party imports, and local imports.

    # Correct:
    import os
    import sys
    
    from flask import Flask
    
    from mymodule import myfunction
    
    # Wrong:
    import sys, os
    
  • Absolute Imports: Absolute imports are recommended for clarity and better error handling. Explicit relative imports are acceptable in some cases, particularly in complex package layouts.

    # Correct:
    from mymodule import myfunction
    
    # Also Correct (for complex layouts):
    from .mymodule import myfunction
    
  • Wildcard Imports: Wildcard imports should generally be avoided as they make it unclear which names are present in the namespace.

    # Correct:
    from math import pi
    
    # Wrong:
    from math import *
    
    # Wrong:
    from mymodule import *
    

🧩 Module Level Dunder Names

  • Placement: Module-level β€œdunders” (e.g., __all__, __author__, __version__) should appear after the module docstring but before any import statements, except for from __future__ imports.
    """
    This is the example module.
    """
    
    from __future__ import print_function
    
    __all__ = ['foo', 'bar']
    __version__ = '1.0'
    __author__ = 'Your Name'
    
    import os
    

πŸ“ String Quotes

  • Consistency: Single and double quotes are equivalent in Python; choose one and stick with it. Use triple double quotes for docstrings to align with PEP 257.
    # Correct:
    name = "John"
    name = 'John'
    
    # Correct (docstring):
    def foo():
        """This is a docstring."""
        pass
    

βšͺ Whitespace in Expressions and Statements

  • Avoid Extraneous Whitespace: Avoid unnecessary whitespace inside parentheses, brackets, braces, and before commas, colons, or semicolons.

    # Correct:
    spam(ham[1], {eggs: 2})
    
    # Wrong:
    spam( ham[ 1 ], { eggs: 2 } )
    
  • Binary Operators: Surround binary operators with a single space on both sides, except when used for alignment.

    # Correct:
    x = 1 + 2
    
    # Wrong:
    x=1+2
    
  • Spacing for Matrix Brackets: In slices, treat colons as binary operators, with equal spacing on either side. In extended slices with omitted parameters, omit the space around colons:

    # Correct:
    ham[1:9], ham[1:9:3]
    
    # Wrong:
    ham[1: 9], ham[1 :9]
    
  • Function Calls and Indexing: Do not add space immediately before the open parenthesis that starts the argument list of a function call or indexing/slicing:

    # Correct:
    spam(1)
    dct['key'] = lst[index]
    
    # Wrong:
    spam (1)
    dct ['key'] = lst [index]
    
  • Assignment Operators: Use a single space around assignment operators (=) for assignments, except when aligning multiple assignments to improve readability:

    # Correct:
    x = 1
    y = 2
    
    # Wrong:
    x             = 1
    y             = 2
    

πŸ“ Comments

  • Block Comments: Use block comments to explain sections of code, formatted as complete sentences. Each line of a block comment starts with a # and a single space.

    # Correct:
    # This block comment explains the following code section.
    x = x + 1
    
    # Wrong:
    #This block comment is not properly formatted.
    x = x + 1
    
  • Inline Comments: Use inline comments sparingly, separating them from the code by at least two spaces. They should not state the obvious.

    # Correct:
    x = x + 1  # Increment x
    
    # Wrong:
    x = x + 1  #Increment x
    
    # Wrong (obvious):
    x = x + 1  # Add one to x
    
  • Documentation Strings: Follow PEP 257 for docstring conventions. Write docstrings for all public modules, functions, classes, and methods. Multiline docstrings should have the closing """ on a line by itself.

    # Correct:
    def foo():
        """
        This is a multiline docstring.
        It describes the function's purpose.
        """
        pass
    
    # Wrong:
    def foo():
        """This is a one-line docstring."""
        pass
    

πŸ”€ Naming Conventions

  • Descriptive Names: Choose descriptive names that convey the purpose of the variable, function, or class.

    # Correct:
    def calculate_area(radius):
        pass
    
    # Wrong:
    def foo(x):
        pass
    
  • Case Styles: Use lower_case_with_underscores for functions and variables, CapWords (CamelCase) for classes, and UPPER_CASE_WITH_UNDERSCORES for constants.

    # Correct:
    def my_function():
        my_variable = 10
    
    class MyClass:
        pass
    
    MAX_OVERFLOW = 100
    
  • Underscores: Use a single leading underscore for non-public methods and instance variables. Use double leading underscores to invoke name mangling for attributes intended to be private in subclasses.

    # Correct:
    class MyClass:
        def _private_method(self):
            pass
    
    class MyClass:
        def __private_method(self):
            pass
    

πŸ”§ Programming Recommendations

  • Comparison Operators: Use is or is not for comparisons to None, and isinstance() for type checks.

    # Correct:
    if obj is None:
        pass
    
    if isinstance(obj, int):
        pass
    
    # Wrong:
    if obj == None:
        pass
    
    if type(obj) is int:
        pass
    
  • Boolean Values: Do not compare boolean values to True or False using ==:

    # Correct:
    if greeting:
        pass
    
    # Wrong:
    if greeting == True:
        pass
    
  • Return Statements: Be consistent with return statements within a function. If a function returns a value in some cases, ensure all paths return a value, explicitly using return None when necessary.

    # Correct:
    def foo(x):
        if x >= 0:
            return x
        return None
    
    # Wrong:
    def foo(x):
        if x >= 0:
            return x
    
  • Lambda Expressions: Prefer def over lambda for function definitions, as it provides better readability and debugging information.

    # Correct:
    def add(x, y): return x + y
    
    # Wrong:
    add = lambda x, y: x + y
    

πŸ”§ Programming Recommendations (continued)

  • Exception Handling: Catch specific exceptions rather than using a bare except: clause. Use exception chaining appropriately with raise X from Y.

    # Correct:
    try:
        result = x / y
    except ZeroDivisionError:
        print("Division by zero is not allowed.")
        raise
    
    # Correct (chaining):
    try:
        value = some_function()
    except ValueError as e:
        raise CustomError("An error occurred") from e
    
    # Wrong:
    try:
        result = x / y
    except:
        print("An error occurred.")
    
  • Context Managers: Use with statements for managing resources to ensure proper cleanup.

    # Correct:
    with open('file.txt', 'r') as file:
        content = file.read()
    
    # Wrong:
    file = open('file.txt', 'r')
    try:
        content = file.read()
    finally:
        file.close()
    
  • Function Annotations: Follow PEP 484 for function annotations. Use PEP 526 for variable annotations, ensuring proper spacing around colons and equal signs:

    # Correct:
    def munge(input: AnyStr = None):
        pass
    
    code: int = 123
    
    # Wrong:
    def munge(input:AnyStr=None):
        pass
    
    code:int = 123
    

πŸ› οΈ Designing for Inheritance

  • Public vs. Non-Public: Distinguish between public and non-public attributes in classes. Public attributes should be accessible and stable, while non-public attributes are subject to change and should be marked with leading underscores.

    # Correct:
    class MyClass:
        def public_method(self):
            pass
    
        def _non_public_method(self):
            pass
    
  • Name Mangling: Use double leading underscores to avoid name clashes in subclasses, though be cautious of potential drawbacks in debugging and introspection.

    # Correct:
    class MyClass:
        def __private_method(self):
            pass
    
    class SubClass(MyClass):
        def __private_method(self):
            pass  # This will not override MyClass's __private_method
    

🌐 Public and Internal Interfaces

  • Public Interfaces: Documented interfaces are considered public unless explicitly marked otherwise. Use the __all__ attribute to define the public API of a module.

    # Correct:
    __all__ = ['public_function', 'PublicClass']
    
    def public_function():
        pass
    
    class PublicClass:
        pass
    
    def _internal_function():
        pass
    
  • Internal Interfaces: Prefix internal names with a single leading underscore to indicate that they are not part of the public API.

    # Correct:
    _internal_variable = 42
    
    def _internal_function():
        pass
    
  • Imported Names: Imported names should be considered an implementation detail. Other modules should not rely on indirect access to such names unless they are explicitly documented as part of the public API.

    # Correct:
    from mymodule import _internal_function
    
    # Usage only within the module that imported it:
    _internal_function()
    

πŸ’» Programming Practices

  • String Methods: Use .startswith() and .endswith() instead of slicing for checking prefixes or suffixes.

    # Correct:
    if filename.startswith('file_'):
        pass
    
    # Wrong:
    if filename[:5] == 'file_':
        pass
    
  • Type Comparisons: Use isinstance() for type comparisons rather than comparing types directly:

    # Correct:
    if isinstance(obj, int):
        pass
    
    # Wrong:
    if type(obj) is int:
        pass
    
  • Sequences: For sequences (strings, lists, tuples), use the fact that empty sequences are false:

    # Correct:
    if not seq:
        pass
    
    # Wrong:
    if len(seq) == 0:
        pass
    
  • Avoid Trailing Whitespace: Avoid trailing whitespace as it can be confusing and lead to subtle bugs.

    # Correct:
    message = "Hello, World!"
    
    # Wrong (note the trailing space after the exclamation point):
    message = "Hello, World! "
    
  • Return in Finally Blocks: Avoid using return, break, or continue in a finally block as it can interfere with exception propagation.

    # Wrong:
    def foo():
        try:
            return 1
        finally:
            return 2  # This will override the previous return
    
  • Consistent Returns: Be consistent in the use of return statements; if any return in a function returns a value, all should, and return None should be explicit when no value is returned.

    # Correct:
    def foo(x):
        if x >= 0:
            return x
        return None
    
    # Wrong:
    def foo(x):
        if x >= 0:
            return x
    
  • Use of Annotations: Follow PEP 484 for function and variable annotations. Variable annotations should have proper spacing around colons and equal signs, consistent with the style guide.

    # Correct:
    def add(a: int, b: int) -> int:
        return a + b
    
    x: int = 10
    
    # Wrong:
    def add(a:int,b:int)->int:
        return a + b
    
    x:int=10
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment