Skip to content

PEP 8 & Style Interview Questions & Answers

15 questions Updated 2026-06-18 Share:

Python interview questions on PEP 8, naming conventions, imports and line length, the Zen of Python, formatters and linters like black and ruff, and when it is acceptable to break PEP 8.

Read the in-depth guidePython PEP 8 & Style Explained — Naming, Layout, and Writing Idiomatic Code(opens in new tab)
15 of 15

PEP 8 is the official style guide for Python code — a set of conventions for formatting and naming that make code consistent and readable across the community. It's a recommendation, not a language rule: code that violates PEP 8 still runs fine, but consistency aids collaboration.

# PEP 8 style
def calculate_total(items, tax_rate=0.0):
    subtotal = sum(items)
    return subtotal * (1 + tax_rate)

# not PEP 8 — cramped, inconsistent spacing/naming
def calcTotal(items,taxRate=0.0):
    subTotal=sum(items);return subTotal*(1+taxRate)

PEP 8 covers indentation (4 spaces), naming, whitespace, imports, and line length. Its guiding principle: readability counts, since code is read far more often than it's written.

PEP 8 assigns a distinct case to each kind of name so readers can tell them apart at a glance. snake_case for functions, variables, and modules; PascalCase (CapWords) for classes; UPPER_SNAKE_CASE for constants. A single leading underscore signals "internal".

MAX_RETRIES = 3                  # constant — UPPER_SNAKE_CASE

class HttpClient:                # class — PascalCase
    def send_request(self):      # method — snake_case
        retry_count = 0          # variable — snake_case
        self._session = None     # _leading underscore = "internal"

Avoid single-character names like l, O, I (they look like digits). Method/ function names use the same snake_case as variables; only classes and exceptions use PascalCase.

Imports go at the top of the file, one per line, grouped in order: standard library, third-party, then local — separated by blank lines. For line length, PEP 8 recommends a maximum of 79 characters (72 for docstrings/ comments), though many modern teams relax this to 88 or 100.

# standard library
import os
import sys

# third-party
import requests

# local
from myapp.utils import helper

# avoid: import os, sys   (multiple on one line)

Keeping imports sorted and grouped makes dependencies obvious. The line-length cap keeps code readable in side-by-side diffs and narrow editors; tools like isort automate import ordering.

The Zen of Python (PEP 20) is a collection of 19 guiding aphorisms that capture Python's design philosophy. You can read it any time by running import this. It informs why the language and its idioms look the way they do.

import this
# Beautiful is better than ugly.
# Explicit is better than implicit.
# Simple is better than complex.
# Readability counts.
# There should be one-- and preferably only one --obvious way to do it.
# ...and 14 more

The aphorisms favor clarity, simplicity, and explicitness over cleverness. They aren't enforced rules but a cultural compass — when two approaches compete, the Zen usually points to the more Pythonic one.

A formatter automatically rewrites your code into a consistent layout; a linter analyzes code and reports style violations and likely bugs without (usually) changing it. black is the dominant formatter (opinionated, near-zero-config); ruff is an extremely fast linter (and formatter) that consolidates many older tools.

# before black
x = {'a':1,'b':2}
# after black
x = {"a": 1, "b": 2}

# command line
# black .          -> reformats files in place
# ruff check .     -> reports lint issues
# ruff check --fix -> auto-fixes what it can

Running a formatter ends style arguments in code review (the tool decides), while a linter catches unused imports, undefined names, and anti-patterns. Most teams run both, often automatically via pre-commit hooks or CI.

PEP 8 itself says "A Foolish Consistency is the Hobgoblin of Little Minds" — style serves readability, so break the rules when following them would make code less readable or when you must stay consistent with surrounding code or an existing API.

# matching an external library's camelCase API
def setUp(self):           # unittest requires this exact name
    ...

# aligning related assignments can aid readability in some cases
x      = 1
longer = 2

Legitimate reasons: compatibility with code that predates PEP 8, conforming to a framework's required names, or when a rule genuinely hurts clarity in context. The rule of thumb: deviate only with a clear readability or compatibility justification, not out of laziness.

Use a single space around binary operators and after commas, but no space inside brackets, before a call's parenthesis, or around = for keyword arguments/defaults. Consistent whitespace is a big part of the PEP 8 look.

x = a + b                  # spaces around operators
f(a, b, c)                 # space after commas, none before "("
d = {"k": 1}               # no space inside braces
def g(x, y=0): ...         # no spaces around = for defaults
result = func(value=10)    # no spaces around = for kwargs

Rule of thumb: spaces around operators and after commas; no spaces hugging brackets or around = in argument lists. Let a formatter enforce it.

Two blank lines between top-level functions and classes; one blank line between methods inside a class. Use blank lines sparingly within functions to separate logical sections.

import os


def first():            # 2 blank lines before top-level defs
    pass


class C:
    def method_a(self):  # 1 blank line between methods
        pass

    def method_b(self):
        pass

Rule of thumb: 2 blank lines around top-level defs/classes, 1 between methods — it visually chunks the file into units.

PEP 257 covers docstring conventions: use triple double-quotes, write a one-line summary as an imperative phrase ending in a period, and for multi-line docstrings put the closing """ on its own line. They're accessible via __doc__.

def fetch(url):
    """Return the response body for the given URL."""   # one-liner

def process(data):
    """Transform and validate the input data.

    Longer explanation of behavior, args, and return value.
    """
    ...

Rule of thumb: every public module/class/function gets a docstring; first line is a concise imperative summary ("Return…", "Compute…"), not "This function…".

Compare to None with is/is not, never ==. Don't compare booleans with == — test truthiness directly. And use if x is not None, not if not x is None, for readability.

if x is None: ...           # good
if x == None: ...           # avoid

if flag: ...                # good
if flag == True: ...        # avoid

if x is not None: ...        # good (not: "if not x is None")

Rule of thumb: is/is not for None; direct truthiness for booleans — explicit == True/== None is noisy and occasionally wrong.

Prefer implicit continuation inside parentheses/brackets/braces over backslashes. Align wrapped elements or use a hanging indent, and put binary operators before the operand on the next line (PEP 8's updated guidance).

# preferred: implicit continuation
total = (first_value
         + second_value
         - third_value)

result = some_function(
    arg_one,
    arg_two,
)

# avoid backslashes:
total = first_value + \
        second_value

Rule of thumb: wrap inside brackets/parens (no backslashes); break before operators and use a trailing comma so diffs stay clean.

Put a space after the colon (not before) in variable/parameter annotations, and spaces around -> for return types. With an annotation, also put spaces around = for defaults (unlike unannotated defaults).

def f(x: int, y: str = "a") -> bool:   # space after :, around ->, around =
    count: int = 0                      # annotated variable
    return True

def g(x, y="a"):                        # no annotation -> no spaces around =
    ...

Rule of thumb: name: type, -> ret, and param: type = default (spaces around = only when the parameter is annotated).

_name = "internal, by convention" (not enforced). __name (leading only) triggers name mangling to avoid subclass clashes. __name__ (dunder) is reserved for Python's own special names — don't invent your own.

class C:
    def __init__(self):
        self.public = 1       # public API
        self._internal = 2    # "don't touch" hint
        self.__mangled = 3    # -> self._C__mangled
# __dunder__ names like __init__, __repr__ are Python's — never make new ones

Rule of thumb: _x for internal, __x only when you need mangling, and never create your own __dunder__ names — they're reserved.

No trailing whitespace, files should end with a single newline, and use spaces, never tabs for indentation (4 per level). Mixing tabs and spaces is a TabError in Python 3.

def f():
    return 1        # 4 spaces, no trailing space after "1"
# file ends with exactly one newline here

Rule of thumb: 4-space indent, no tabs, no trailing whitespace, newline at EOF — all auto-handled by formatters and editor "trim on save" settings.

PEP 8 takes no position on single vs double quotes — just be consistent. Pick one for normal strings and use the other to avoid escaping. Tools like black standardize on double quotes.

name = "Ada"               # black normalizes to double quotes
msg = 'He said "hi"'       # use single to avoid escaping the inner "
sql = "SELECT 'x'"         # use double to avoid escaping the inner '

Rule of thumb: consistency over preference — adopt a formatter's choice (usually double quotes) and switch quote style only to avoid escapes.

More ways to practice

The self-quiz is live. Join our channel for updates, new content & tech tips.

Join our WhatsApp Channel