✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Python Code Style

Python Code Style defines how code is structured, formatted, and written to ensure readability, maintainability, and consistency across projects and teams.

Python code style refers to the consistent visual and naming conventions used to make Python source code easier to read, scan, review, maintain, and recognize without changing the program's intended semantics. These conventions help developers understand code more quickly and avoid subtle bugs or misunderstandings that arise from inconsistent presentation.


Foundations of Python Code Style

Code style encompasses conventions that govern source layout, use of whitespace, line breaking, naming, and import organization. It focuses on how the code is presented visually rather than on correctness or language syntax. While the Python interpreter enforces syntax rules to ensure code executes properly, style guides recommend how code should look to humans for maximum clarity and maintainability.

The principal Python style guide is PEP 8. It emphasizes readability, consistency, and alignment with established surrounding code over mechanical compliance. PEP 8 is not a rigid standard; its guidance may be adapted for clarity or project needs rather than applied dogmatically. The goal is a balance: code should be easy to read and consistent, yet flexible enough to accommodate practical realities.

It is important to distinguish between language-enforced syntax, broadly established Python conventions, project-specific style decisions, and automated formatter defaults. Parser requirements are strict and unambiguous, while style recommendations are softer, aiming for clarity without impacting program semantics.

The following table summarizes major Python code style areas, the readability problems they address, and the conventions used:

Style AspectReadability Problem AddressedTypical Convention
Code layoutVisualizing program structureIndentation, line length, vertical spacing
IndentationIdentifying nested blocks and suitesFour spaces per level, no mixed tabs/spaces
WrappingHandling long lines without breaking readabilityImplicit continuation inside brackets, hanging indent
Vertical spacingDistinguishing logical and structural unitsBlank lines around classes, functions, and blocks
Expression formattingClarity of operator precedence and delimitersStandard spacing around operators and delimiters
Naming conventionsConveying role and intent of identifiersLowercase_with_underscores, CapWords, uppercase constants
Import styleManaging dependencies and namespace clarityGrouped standard, third-party, local imports; explicit

A conceptual representation of a Python source file organized by consistent style is shown below. It illustrates indentation, line wrapping, vertical spacing, expression formatting, naming, and grouped imports as geometric relationships within a file:

Imports import os from sys import argv import numpy as np class DataProcessor: def __init__(self, data): self.data = data def process(self): result = self.data * 2 # Expression formatting return result def main(): dp = DataProcessor([1, 2, 3]) print(dp.process())

Consistency in Python code style operates as a hierarchy of practical concerns. The foremost priority is readability of the immediate code — making it easy to understand for the current reader. Next comes consistency with the surrounding codebase, which reduces cognitive load when switching between files or contributors. Finally, adherence to broader community conventions helps maintain a common "look and feel" across projects, facilitating collaboration and tool integration. Balancing these concerns guides decisions about when to prioritize local clarity over strict conformity or vice versa.


Python Code Layout

Code layout refers to the spatial organization of Python source code through indentation, line length, continuation, blank lines, and related whitespace choices that visually reveal program structure.

Indentation in Python Code

The convention for indentation in Python code is to use four spaces per indentation level. This applies to all suites and nested blocks, such as those inside functions, loops, conditionals, and classes.

Spaces are the preferred indentation mechanism because they provide consistent behavior across different editors and tools. Tabs may be used only when maintaining legacy code consistently indented with tabs. Mixing tabs and spaces for indentation is invalid and can lead to errors or ambiguous parsing.

Well-indented nested control flow example:

def process_items(items):
    for item in items:
        if item > 0:
            print(f"Positive: {item}")
        else:
            print(f"Non-positive: {item}")

Visually confusing indentation example (though syntactically valid if consistent):

def process_items(items):
  for item in items:
        if item > 0:
            print(f"Positive: {item}")
     else:
        print(f"Non-positive: {item}")

In the confusing example, inconsistent indentation widths make it hard to visually parse the control flow, even if Python accepts it.

Continuation indentation is used when breaking long statements across multiple lines. This can be done by vertical alignment or hanging indentation. Continuation lines must be visually distinguishable from the normal indentation level of executable suites.

Long function call with vertical alignment:

result = some_function(long_argument_name_1,
                       long_argument_name_2,
                       long_argument_name_3)

Long function call with hanging indent:

result = some_function(
    long_argument_name_1,
    long_argument_name_2,
    long_argument_name_3
)

Ambiguous continuation indentation (discouraged):

result = some_function(
       long_argument_name_1,
       long_argument_name_2,
       long_argument_name_3
)

Here, the hanging indent lines are not clearly offset from the outer block indentation, obscuring structure.


Line Length and Wrapping in Python Code

Line length limits are a readability and review aid, not a semantic Python restriction. The conservative PEP 8 limit is 79 characters per line for code, with a soft recommendation of 72 for docstrings and comments. Some projects agree on longer limits, such as 99 characters, but consistency within a project is more important than arbitrary local variation.

Implicit line continuation inside parentheses, brackets, and braces is preferred for wrapping long expressions and statements because it makes grouping visually clear.

Examples of implicit continuation:

Long function call:

result = some_function(
    argument_1, argument_2, argument_3,
    argument_4, argument_5
)

Collection literal:

items = [
    "apple", "banana", "cherry",
    "date", "elderberry"
]

Boolean condition:

if (user.is_active and
    user.has_permission and
    not user.is_suspended):
    perform_action()

Context manager statement:

with open("file.txt") as f, \
     open("log.txt", "a") as log_file:
    process(f, log_file)

Explicit backslash continuation (shown in the context manager example) is more fragile and generally discouraged when implicit continuation can be used instead.

Line breaking around binary operators should be visually consistent, keeping related operands and operators easy to scan. A project-consistent layout is preferred over mixing styles within one expression.


Blank Lines and Vertical Spacing in Python Code

Blank lines serve as visual separators for structural and logical units, not as decorative whitespace.

The conventions are:

  • Two blank lines surrounding top-level class and function definitions.
  • One blank line between method definitions inside a class.
  • Sparing blank lines inside functions to separate meaningful logical stages.

Example demonstrating vertical spacing:

import os
import sys

class Example:
    
    def method_one(self):
        do_something()
        
    def method_two(self):
        do_something_else()


def main():
    example = Example()
    example.method_one()
    example.method_two()

Python Expression and Statement Formatting

Expression and statement formatting concerns the placement of whitespace, delimiters, operators, commas, colons, and related syntax so that the visual structure corresponds closely to Python expression structure.

Extraneous whitespace immediately inside parentheses, brackets, and braces, and immediately before commas, semicolons, and ordinary colons should be avoided.

Well-spaced call:

result = function(arg1, arg2)

Unnecessarily spaced call:

result = function( arg1 , arg2 )

Well-spaced indexing:

value = array[0]

Unnecessarily spaced indexing:

value = array[ 0 ]

Well-spaced collection display:

items = [1, 2, 3]

Unnecessarily spaced collection display:

items = [ 1 , 2 , 3 ]

Spacing around assignment, comparison, Boolean, and arithmetic operators should follow conventional rules, with allowance for tighter spacing when it clarifies operator precedence.

Examples:

x = 5
if x == 10:
    print("Ten")

flag = (a and b) or not c
sum = x + y - z

def func(arg1=10, arg2=None):
    pass

Assignment operators in ordinary statements use spaces around the equals sign. For keyword arguments and unannotated default parameter values, no spaces are used around the equals sign.

Syntax ElementWhitespace Convention
Assignment (=)Spaces before and after
Keyword argumentsNo spaces around =
Default parameter valuesNo spaces around =
Annotations (:)No space before colon, one space after
Slicing colons (:)No spaces or spaces consistent for clarity
CommasNo space before, one space after
Arithmetic/comparison operatorsSpaces before and after

Annotation formatting example:

def greet(name: str) -> None:
    greeting: str = f"Hello, {name}"
    print(greeting)

Slicing colon formatting treats colons as operators. Their spacing depends on clarity with omitted or present bounds:

slice_1 = data[1:5]
slice_2 = data[:5]
slice_3 = data[1:]
slice_4 = data[1:5:2]

Trailing commas are useful in vertically formatted multi-line collections, calls, imports, and signatures because they support stable diffs and straightforward extension. They differ from required singleton-tuple commas.

Examples:

numbers = [
    1,
    2,
    3,
]

def func(
    a,
    b,
    c,
):
    pass

In contrast, omitting trailing commas in such contexts can cause diffs to be less stable.

Consistency in quote choice, avoiding unnecessary escape characters, and restrained use of multiple statements on one physical line improves readability. Python does not enforce a single versus double quote rule; readability and consistency within a project guide the choice.


Python Naming Conventions

Naming conventions in Python serve as visual signals to communicate the role and intended use of identifiers—such as modules, packages, classes, functions, methods, variables, constants, exceptions, and interfaces—making code more understandable.

Module names should be short and lowercase, with underscores used only when they materially improve readability. Package names follow similar conventions, favoring short lowercase names without underscores unless clarity demands them.

Classes and class-like exceptions use CapWords (also known as PascalCase) naming. Exception classes typically use the suffix Error to indicate their role.

Functions, methods, and ordinary variables use lowercase names with underscores separating words (snake_case), emphasizing descriptive names whose abstraction level matches their role.

Constants at the module level are named in uppercase letters with underscores, signaling their intended constancy rather than runtime immutability.

The first parameter of instance methods is conventionally named self, while the first parameter of class methods is named cls.

Identifier TypeNaming ConventionExample
Modulesshort lowercase, underscores if neededemail, http_client
Packagesshort lowercase, no underscores preferredmypackage
ClassesCapWordsDataProcessor
ExceptionsCapWords with Error suffixValueError
Functionslowercase_with_underscoresprocess_data
Methodslowercase_with_underscorescalculate_sum
Variableslowercase_with_underscorestotal_count
ConstantsALL_CAPS_WITH_UNDERSCORESMAX_RETRIES
Instance method first paramselfdef method(self):
Class method first paramcls@classmethod def method(cls):

Using a single trailing underscore is a conventional way to avoid collisions with Python keywords when a clear synonym is not preferable.

Examples contrasting clear and problematic names:

# Clear conventional names
def calculate_area(radius):
    pass

class UserError(Exception):
    pass

MAX_SIZE = 100

# Misleading or inconsistent names
def calcArea(r):  # inconsistent casing and ambiguous abbreviation
    pass

class usererror(Exception):  # lowercase class name
    pass

maxSize = 100  # lowercase constant

A single leading underscore prefixes a module, function, method, or attribute name to mark it as non-public or implementation-oriented, signaling it is intended for internal use rather than enforcing access control.

Double-leading underscores in class attributes trigger name mangling, primarily to reduce accidental subclass name collisions. This mechanism is distinct from an ordinary style convention for privacy.

Reserved double-leading-and-trailing names (sometimes called dunder names) are associated with Python-defined special protocols (e.g., __init__, __str__). Developers should avoid inventing arbitrary new dunder-style names for ordinary application concepts.

Single-character names like lowercase l, uppercase O, and uppercase I should be avoided because they can be visually confused with numeric glyphs 1 and 0.

Consistency with an established public API or mature codebase can justify retaining legacy naming conventions when renaming would reduce compatibility or introduce needless inconsistency.


Python Import Style

Import style governs placement, grouping, spelling, qualification, and namespace exposure of imported modules and objects, ensuring dependencies remain easy to identify and names understandable at use sites.

Imports are conventionally placed near the beginning of a module, after any module metadata such as docstrings, and before ordinary module-level definitions. Local imports inside functions or blocks are acceptable only when justified by runtime or dependency considerations.

Imports are grouped into three categories, separated by blank lines:

  1. Standard library imports
  2. Third-party imports
  3. Local application or library imports

Example of a well-structured import block:

import os
import sys

import requests
import numpy as np

from myproject.utils import helper_function
from myproject.models import DataModel

In contrast, a mixed unstructured block might look like:

import requests
from myproject.models import DataModel
import os
from myproject.utils import helper_function
import numpy as np
import sys

Prefer placing ordinary import module statements on separate lines. When explicitly importing multiple names from one module, a single from module import ... statement is allowed if readable.

Import FormNamespace EffectReadabilityQualificationPrincipal Style Consideration
import moduleImports entire module namespaceHigh (module prefix required)Explicit qualificationClear origin of names
from module import nameImports specific names directlyModerate (no prefix)No qualificationConvenience vs clarity
Aliased importsRenames imported module or nameVariable, depends on aliasAlias may obscure originResolving collisions or conventions
Absolute importsExplicit full path from project rootClear and explicitUsually preferredAvoid ambiguity
Explicit relative importsRelative to current module/packageClear within local packageLocal clarityReadability of package structure
Wildcard imports (*)Imports all public namesLow (origin unclear)Not qualifiedDiscouraged except rare cases

Absolute imports are generally clearer and more explicit. Explicit relative imports are legitimate within packages when they improve local package readability.

Import aliases are useful for resolving meaningful name collisions or following well-established conventional abbreviations (e.g., import numpy as np). However, aliases that obscure recognizable names are discouraged.

Wildcard imports reduce namespace clarity and interfere with readers' and tools' ability to determine where names originate. They are tolerated only for narrowly defined interface-republication cases.

Examples contrasting import styles:

import os                     # Explicit qualification
from math import sqrt         # Selective import
import numpy as np            # Justified alias
from module import *          # Wildcard import (discouraged)

The first three forms make the provenance of names immediately visible, while the wildcard form obscures it.

Import formatting should preserve recognizable dependency boundaries and avoid unnecessary churn such as arbitrary reordering or grouping differences that conflict with an established project-wide convention.


Solved Python Code Style Exercise

Poorly styled source example:

import sys,os
from numpy import array as arr,random
import mymodule
def  foo(x,y):print(x+y)
def bar( x , y ):
  if x>y:
      return x-y
  else:
    return y-x
class myclass:
 def __init__(self,val):
  self.val=val
 def method(self):
   print( self.val )

This code has inconsistent indentation (mix of 2 and 1 spaces), awkward line wrapping (one-liner foo), excessive or missing vertical spacing, inconsistent operator whitespace, weak naming (e.g., myclass), and disorganized imports (no grouping, multiple imports on one line).


Restyled version preserving behavior:

import os
import sys

import numpy as np

import mymodule


def foo(x, y):
    print(x + y)


def bar(x, y):
    if x > y:
        return x - y
    else:
        return y - x


class MyClass:

    def __init__(self, val):
        self.val = val

    def method(self):
        print(self.val)

Step-by-step restyling explanation:

  • Layout and indentation: Indentation normalized to four spaces per level. Code blocks clearly outlined.
  • Line wrapping: One-liner foo expanded to multiple lines for clarity.
  • Blank lines: Two blank lines added around top-level definitions. One blank line added between methods.
  • Expression formatting: Spaces added around operators and commas for readability.
  • Naming: Class renamed to MyClass to follow CapWords convention. Function and parameter names use lowercase with underscores consistently.
  • Imports: Grouped imports by standard library, third-party (numpy), and local (mymodule), each on its own line.
  • Other conventions: Removed trailing spaces and unnecessary in-line commands.

Changes to indentation and blank lines are purely stylistic and do not affect Python syntax or semantics. Changing import aliases or names could affect behavior if done incorrectly; no such changes were made here except for formatting.


This completes the comprehensive overview of Python code style principles and practices.