Python Programming11 min read•Published 2026-09-26

Python Style Guide (PEP 8) – Complete Beginner Guide

Master PEP 8, the official Python style guide: naming conventions, code layout, indentation, docstrings, imports, line length limits, Ruff & Black tools, and good vs bad code examples.

Er. Sumit Kumar
Er. Sumit Kumar
Founder & Lead Technical Mentor
Python Style Guide (PEP 8) – Complete Beginner Guide

When programmers start learning Python, one of the first things they fall in love with is how remarkably readable and expressive the language is. Unlike C, C++, or Java, Python has no curly braces {} to denote code blocks and no mandatory semicolons ; at the end of every line.

However, this visual simplicity comes with a shared responsibility: to keep Python code clean, uniform, and effortless to read for humans.

That is precisely where PEP 8 comes in. Whether you are submitting college lab assignments, creating open-source repositories on GitHub, or interviewing for a software engineering role, adhering to PEP 8 is what separates an amateur coder from a polished, hireable Python developer.

In this exhaustive beginner guide by Er. Sumit Kumar at MSK Institute, you will learn everything you need to know about PEP 8—from naming rules and whitespace mechanics to automated tools like Ruff, Black, and Flake8.


1. What is PEP 8?

The acronym PEP stands for Python Enhancement Proposal. A PEP is a design document providing information to the Python community, describing a new feature, process, or environment for Python.

PEP 8 is the eighth enhancement proposal ever written for Python, authored in 2001 by Guido van Rossum (the creator of Python), Barry Warsaw, and Nick Coghlan.

The primary purpose of PEP 8 is to provide the official coding style guide for the Python standard library. Over the last two decades, it has evolved into the universal, de-facto style standard adopted by every professional Python team, open-source project, and enterprise worldwide.

Python
# The Zen of Python (PEP 20) by Tim Peters
import this

If you open the MSK Code Playground and run import this, the very first lines you will see are:

- "Beautiful is better than ugly. - Explicit is better than implicit. - Simple is better than complex. - Readability counts."

PEP 8 is the practical, day-to-day blueprint that translates those Zen philosophical axioms into concrete formatting rules.


2. Why PEP 8 is Important

Why should you spend time worrying about spaces, line lengths, and letter cases when your code already runs and produces the correct output?

Key Principle: "Code is read much more often than it is written." — Guido van Rossum

Here is why writing PEP 8 compliant code is non-negotiable:

1. Human Readability & Lower Cognitive Load

Developers spend approximately 75% of their working hours reading, debugging, and reviewing existing code, and only 25% writing fresh code. When code adheres to uniform styling, your brain does not have to spend energy decoding erratic indents or weird capitalization—you can focus entirely on business logic.

2. Frictionless Team Collaboration

In professional development teams, multiple engineers commit code to the same repository daily. If one developer uses 2 spaces, another uses tabs, and a third creates 160-character single-line monstrosities, Git diffs become noisy nightmares and pull request reviews become frustrating debates over syntax aesthetics instead of software logic.

3. Fewer Indentation Bugs

Because Python relies on whitespace indentation to define scope and control flow (unlike languages with {} blocks), sloppy spacing can lead to catastrophic bugs. Adhering to strict 4-space indentation eliminates accidental indentation leaks. Learn more about structure in our Python Syntax and Code Structure Tutorial.

4. Technical Job Interviews & Code Audits

When technical mentors or hiring managers review your GitHub portfolio or coding test submissions, PEP 8 compliance is the first indicator of software maturity. A script with erratic naming (X1, calc_VAL, getData) instantly signals an inexperienced candidate, while clean PEP 8 code signals industry readiness.


3. Python Naming Conventions: The Complete Cheat Sheet

Python uses different casing conventions depending on whether an identifier represents a variable, a function, a class, or a module. Memorize this reference table:

Identifier TypePEP 8 ConventionValid ExampleAvoid
Variablessnake_casestudent_name, total_scorestudentName, TotalScore
Functionssnake_case (with verbs)calculate_tax(), get_user_by_id()CalculateTax(), calcTax()
ClassesPascalCase / CapWordsStudentProfile, PaymentGatewaystudent_profile, studentProfile
ConstantsSCREAMING_SNAKE_CASEMAX_CONNECTIONS, DEFAULT_TIMEOUTmaxConnections, max_connections
Modules (.py)short_snake_casemath_utils.py, student_service.pyMathUtils.py, student-service.py
Packagesshort_lower (no underscores)mskinstitute, analyticsmsk_institute, Analytics
Methodssnake_casedef process_payment(self):def ProcessPayment(self):
Protected Member_single_leading_underscore_internal_cache, _sync_state()—
Private Member__double_leading_underscore__auth_token (name mangling)—
Keyword Clashsingle_trailing_underscore_class_, id_, type_klass, my_class

4. Functions, Classes, and Constants Naming in Detail

Let's dive into the specifics of naming each core Python building block:

Functions and Methods

Functions perform actions; therefore, their names should almost always begin with an active verb followed by a descriptive noun in snake_case:

Bad function names

Python
def calc(p, d): # Too cryptic; what are p and d?
pass
 
def StudentData(): # PascalCase is reserved for classes!
pass
 
def do_stuff(): # Vague; conveys zero business purpose
pass

Good function names (Action + Subject)

Python
def calculate_discount(price: float, discount_percent: float) -> float:
return price * (1.0 - discount_percent / 100.0)
 
def find_active_students(batch_id: str) -> list[dict]:
pass
 
def is_admission_open(course_id: str) -> bool:
pass
Pro Tip
For boolean-returning helper functions (predicates), prefix the name with is_, has_, can_, or should_ (e.g., is_eligible, has_permission, can_submit). Read our full Python Functions Tutorial for deeper examples.

Classes and Exceptions

Class names should use PascalCase (also called CapWords), where every word starts with a capital letter with no underscores:

Python
# Good Class Definitions
class StudentRecord:
def __init__(self, full_name: str, enrollment_number: str) -> None:
self.full_name = full_name
self.enrollment_number = enrollment_number
 
class PaymentGatewayAdapter:
pass
 
# Custom Exceptions: Must always inherit from Exception and end with 'Error'
class StudentNotFoundError(Exception):
"""Raised when a queried student registration ID does not exist."""
pass
 
class InsufficientBalanceError(Exception):
"""Raised when transaction amount exceeds current wallet balance."""
pass

Constants

Python does not possess a strict compiler-level const keyword like C++ or JavaScript. Instead, the Python community signals that a variable must never be mutated by typing it in SCREAMING_SNAKE_CASE at the module level.

Python
# Constants defined at the top of the file
INSTITUTE_NAME = "MSK Institute"
MAX_LOGIN_ATTEMPTS = 5
DEFAULT_DATABASE_TIMEOUT_SECONDS = 30
GST_RATE_PERCENTAGE = 18.0
 
# Never mutate a constant later in your code!

Check out our dedicated tutorial on Python Constants Convention and Python Variables and Rules.


5. Indentation and Spacing: The Golden Rules

Rule 1: Always 4 Spaces per Indentation Level

PEP 8 is unequivocal: use 4 spaces per indentation level. Never use 2 spaces, never use 8 spaces, and never use tabs.

Python 3 strictly disallows mixing tabs and spaces for indentation in the same file. If you mix them, Python will halt execution with a TabError:

Output
TabError: inconsistent use of tabs and spaces in indentation
Pro Tip
In VS Code, open your settings (Ctrl + ,), search for Editor: Insert Spaces, and check it. Then set Editor: Tab Size to 4. When you press the Tab key, VS Code will automatically insert 4 spaces for you.

Rule 2: Hanging Indents and Line Continuation

When a statement, function header, or list is too long to fit on one line, use hanging indents with Python’s implied line continuation inside parentheses (), brackets [], or braces {}.

Python
# Good: Aligned with the opening delimiter
def enroll_student(student_id: str,
course_slug: str,
batch_timing: str,
scholarship_awarded: bool = False) -> bool:
return True
 
# Good: Hanging indent with 4 extra spaces and closing paren on its own line
def calculate_aggregate_marks(
physics_score: float,
chemistry_score: float,
mathematics_score: float,
computer_science_score: float,
) -> float:
total = (
physics_score
+ chemistry_score
+ mathematics_score
+ computer_science_score
)
return total / 4.0
 
# Bad: First argument on the first line with unaligned subsequent lines
def enroll_student(student_id: str,
course_slug: str, batch_timing: str):
pass

Rule 3: Blank Lines

Blank lines give breathing room to code so readers can discern logical sections:

  • Surround top-level functions and class definitions with two blank lines.
  • Surround method definitions inside a class with one blank line.
  • Use blank lines sparingly inside functions to separate distinct computational steps (like input validation, processing, and output formatting).
Python
import math
 
 
class GeometryCalculator:
"""Calculates shapes and surface boundaries."""
 
def __init__(self, unit: str = "cm") -> None:
self.unit = unit
 
def circle_area(self, radius: float) -> float:
# Step 1: Input validation
if radius < 0:
raise ValueError("Radius cannot be negative.")
 
# Step 2: Compute area
return math.pi * (radius ** 2)
 
 
def print_banner(title: str) -> None:
print(f"=== {title} ===")

Rule 4: Whitespace in Expressions and Statements

Pay close attention to where spaces should and should not be placed:

1. Immediately inside parentheses, brackets, or braces:

Python
# Good:
spam(ham[1], {eggs: 2})
# Bad:
spam( ham[ 1 ], { eggs: 2 } )

2. Immediately before a comma, semicolon, or colon:

Python
# Good:
if x == 4: print(x, y); x, y = y, x
# Bad:
if x == 4 : print(x , y) ; x , y = y , x

3. Around assignment (=) and comparison (==, !=, <, >, <=, >=) operators:

Python
# Good:
score = 95
is_passed = score >= 40
# Bad:
score=95
is_passed=score>=40

4. Default parameter values in function definitions (NO spaces):

Python
# Good:
def query_course(slug: str, include_batches=True):
pass
# Bad:
def query_course(slug: str, include_batches = True):
pass
 
# Exception: When combining default value with a type annotation, use spaces:
# Good:
def query_course(slug: str, include_batches: bool = True):
pass

6. Imports and Line Length Guidelines

Maximum Line Length: 79 Characters

PEP 8 limits all lines to a maximum of 79 characters. For flowing blocks of text such as docstrings or block comments, the limit is 72 characters.

Why 79 characters?

In modern software development, developers frequently keep two to three editor tabs open side-by-side on widescreen monitors, or compare git diffs in split-screen pull request reviews. Limiting lines to 79 characters prevents awkward horizontal scrolling and keeps diffs cleanly visible on any screen or terminal.

Python
# Breaking long strings or mathematical operations cleanly:
# Good: Use parentheses for line continuations
report_message = (
f"Student {student_name} (ID: {enrollment_id}) has successfully completed "
f"the Python for Beginners certification cohort with Distinction."
)
 
# Bad: Escaped newlines with backslashes
report_message = "Student " + student_name + " has completed " + \
"the course successfully with distinction."

How to Organize Imports

Imports must always be placed at the very top of your file, immediately after any module docstrings and comments, and before module globals or code.

PEP 8 mandates grouping imports into three distinct sections, separated by a single blank line:

  1. 1
    Standard Library Imports (modules bundled with Python: os, sys, math, datetime, pathlib)
  2. 2
    Related Third-Party Imports (installed packages: requests, numpy, pandas, fastapi)
  3. 3
    Local Application / Library Imports (your custom project modules)
Python
"""Module for managing student admissions and invoice generation."""
 
# 1. Standard library imports
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
 
# 2. Related third-party imports
import requests
from fastapi import HTTPException, status
from pydantic import BaseModel, EmailStr
 
# 3. Local application/library-specific imports
from msk.config import DATABASE_URL
from msk.models.student import StudentRecord
from msk.utils.security import hash_password

Import Rules to Remember:

  • One import per line: Write import os and import sys on separate lines. Do not write import os, sys.
  • Allowed grouping: from subprocess import Popen, PIPE is completely valid.
  • Never use wildcard imports: Avoid from math import *. Wildcard imports obscure which names are present in the namespace, confuse linters, and cause silent variable shadowing bugs.

7. Comments and Docstrings (PEP 257)

Comments that contradict the code are worse than no comments at all. When code changes, always keep comments updated!

Inline Comments

An inline comment is a comment on the same line as a statement. PEP 8 specifies:

  • Separate the comment from the statement by at least two spaces.
  • Start with # (a hash symbol followed by a single space).
  • Explain WHY, not WHAT: Never state the obvious.
Python
# Bad: Redundant obvious comment
count = count + 1 # Add 1 to count
 
# Good: Explains business rationale or domain logic
# Offset index by 1 to match official 1-based roll numbers in legacy university records
display_roll_number = student_index + 1

For more guidance, check our Python Comments and Best Practices Guide.

Docstrings (PEP 257)

Docstrings are string literals that appear as the first statement in a module, function, class, or method. Unlike regular comments, docstrings are retained at runtime and can be inspected via help(function_name) or function_name.__doc__.

  • Always use triple double-quotes: """...""".
  • For one-line docstrings, keep the closing quotes on the same line.
  • For multi-line docstrings, document arguments (Args:), return values (Returns:), and potential errors (Raises:).
Python
def calculate_compound_interest(
principal: float,
annual_rate: float,
years: int,
compounding_frequency: int = 12,
) -> float:
"""Calculate the future value of an investment using compound interest.
 
Args:
principal: Initial invested amount in INR. Must be greater than 0.
annual_rate: Annual nominal interest rate expressed as a decimal (e.g., 0.08 for 8%).
years: The number of years the funds are invested.
compounding_frequency: Number of times interest compounds per year. Default is 12 (monthly).
 
Returns:
The total accumulated amount (principal + interest) rounded to 2 decimal places.
 
Raises:
ValueError: If principal or years is negative.
"""
if principal <= 0 or years < 0:
raise ValueError("Principal and years must be positive numerical values.")
 
rate_per_period = annual_rate / compounding_frequency
total_periods = compounding_frequency * years
future_value = principal * ((1.0 + rate_per_period) ** total_periods)
 
return round(future_value, 2)

8. Good vs Bad Code Examples: Side-by-Side Comparison

Let's inspect how a typical unformatted script transforms into clean, professional PEP 8 code.

The Bad Code (Violates PEP 8)

Python
# A messy script with multiple PEP 8 violations
import math,os,sys
from datetime import *
 
MaxScore=100
global_tax_val = 0.18
 
def Calc_grade(Score,bonus = 5):
FinalScore = Score+bonus
if FinalScore>=90:
return 'A'
elif FinalScore>=75:
return 'B'
else: return 'C'
 
class student_data:
def __init__(Self,NAME,RollNo):
Self.NAME=NAME
Self.RollNo=RollNo
def printData(Self):
print("Name: " + Self.NAME + ", Roll: " + str(Self.RollNo))
 
s = student_data("Amit Sharma", 101)
if s.RollNo == None:
print("No roll number")

What makes this code bad?

  • Multiple imports on a single line (import math,os,sys).
  • Dangerous wildcard import (from datetime import *).
  • Inconsistent naming: MaxScore is PascalCase instead of uppercase constant MAX_SCORE.
  • Non-standard function name: Calc_grade mixes title case and underscores.
  • Spaces around default argument: bonus = 5.
  • Missing whitespace around binary operator: Score+bonus.
  • Class name student_data uses snake_case instead of PascalCase StudentData.
  • Non-standard instance identifier Self instead of lowercase self.
  • Semicolon and statement on same line as else:.
  • == None comparison instead of identity check is None.

The Refactored Code (100% PEP 8 Compliant)

Python
"""Module for student grading and record representation."""
 
import math
import os
import sys
from datetime import datetime
 
# Module Constants
MAX_SCORE = 100
DEFAULT_TAX_RATE = 0.18
 
 
def calculate_grade(score: float, bonus: float = 5.0) -> str:
"""Calculate the final letter grade considering bonus points.
 
Args:
score: Raw exam mark achieved by student.
bonus: Optional bonus points added to total score.
 
Returns:
Letter grade string: 'A', 'B', or 'C'.
"""
final_score = score + bonus
if final_score >= 90:
return "A"
elif final_score >= 75:
return "B"
return "C"
 
 
class StudentRecord:
"""Encapsulates student profile credentials."""
 
def __init__(self, name: str, roll_number: int | None) -> None:
self.name = name
self.roll_number = roll_number
 
def display_details(self) -> None:
"""Print formatted student identity string."""
print(f"Name: {self.name}, Roll: {self.roll_number}")
 
 
if __name__ == "__main__":
student = StudentRecord("Amit Sharma", 101)
student.display_details()
 
if student.roll_number is None:
print("Registration pending: No roll number assigned.")

Notice how much easier the refactored version is to read, understand, and debug!


9. Modern PEP 8 Tooling: Ruff, Flake8, and Black

Manually memorizing every single spacing and comma rule can feel overwhelming. Fortunately, the Python ecosystem provides world-class automated linters and code formatters.

1. Flake8: The Classic Linter

Flake8 is the longstanding community standard linter. It bundles three tools:

  • PyFlakes (checks for logical errors like undefined variables or unused imports)
  • pycodestyle (checks for PEP 8 formatting violations)
  • mccabe (checks code complexity)
Bash / Terminal
# Install Flake8
pip install flake8
 
# Run Flake8 against your current directory
flake8 .

Flake8 outputs error codes corresponding to PEP 8 rules (e.g., E302: expected 2 blank lines, E501: line too long (88 > 79 characters), W291: trailing whitespace).

2. Black: "The Uncompromising Code Formatter"

While Flake8 warns you about errors, Black actually fixes them automatically. It parses your Python files into an Abstract Syntax Tree (AST) and reformats your code deterministically according to strict PEP 8 principles.

Bash / Terminal
# Install Black
pip install black
 
# Auto-format your entire project directory
black .
Note
Note on Line Length: Black uses a default line length limit of 88 characters instead of PEP 8's 79 characters. The Black maintainers chose 88 as a pragmatic sweet spot that reduces vertical line splitting by ~10% while remaining comfortably readable on modern laptops. You can configure it to 79 using black --line-length 79 ..

3. Ruff: The Ultra-Fast Modern Python Standard

In modern Python development (2024–2026), Ruff has revolutionized code tooling. Built from scratch in Rust by Astral, Ruff is 10 to 100 times faster than Flake8, Black, and isort combined. It replaces dozens of legacy tools with a single unified binary.

Bash / Terminal
# Install Ruff
pip install ruff
 
# 1. Lint and detect PEP 8 violations in milliseconds
ruff check .
 
# 2. Automatically fix safe violations (remove unused imports, organize import order)
ruff check --fix .
 
# 3. Format all code according to Black-compatible PEP 8 formatting
ruff format .

To make PEP 8 compliance effortless:

  1. 1
    Open the VS Code Extensions tab (Ctrl + Shift + X).
  2. 2
    Search for Ruff (by Astral Software) and install it.
  3. 3
    Add this to your .vscode/settings.json:
JSON
{
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
}
}

With this setup, every time you press Ctrl + S, your file will automatically be formatted to PEP 8 standards!


10. PEP 8 vs Coding Best Practices: What's the Difference?

A common misconception among beginner programmers is equating PEP 8 with overall software engineering quality. While closely related, they address two distinct layers:

DimensionPEP 8 (Style Guide)Coding Best Practices (Engineering)
FocusHow code looks (aesthetics, formatting)How code functions & scales (architecture)
Examples4 spaces, snake_case, blank lines, max 79 charsDRY, SOLID principles, design patterns, testing
Enforced ByFormatters & Linters (Ruff, Black, Flake8)Unit tests, architecture design, peer reviews
Bugs PreventedIndentation errors, variable name collisionsMemory leaks, race conditions, unhandled exceptions
ImportanceHigh (Visual clarity & team consistency)Critical (Correctness, security, scalability)

When to Ignore PEP 8: The Golden Caveat

In the opening section of PEP 8, Guido van Rossum explicitly included a famous quote by Ralph Waldo Emerson:

"A Foolish Consistency is the Hobgoblin of Little Minds."

PEP 8 is a guide, not a religious dogma. You should deliberately break PEP 8 in situations where:

  1. 1
    Applying the rule would make the code LESS readable, even for someone used to reading PEP 8 code.
  2. 2
    Maintaining consistency with legacy surrounding code that already uses a different style.
  3. 3
    Preserving backward compatibility with an established public API or external library that uses camelCase.

11. Frequently Asked Questions (FAQ)

Q1: Does following PEP 8 make my Python programs run faster?

No. Python’s bytecode compiler strips out whitespace, indentation, comments, and docstrings when creating .pyc files. A poorly formatted script and a PEP 8 compliant script will execute at the exact same speed. PEP 8 is designed for human execution speed—allowing developers to read, diagnose, and maintain code faster.

Q2: Why does PEP 8 mandate 79 characters when everyone has widescreen 4K displays?

While modern displays can fit 300 characters across a screen, human eyes struggle to track line breaks across very wide blocks of text (the same reason books and newspapers use narrow columns). Furthermore, engineers often view two files side-by-side in split screens while writing unit tests, or review side-by-side pull request diffs on GitHub. 79–88 characters fits comfortably in any split view.

Q3: Why should I write if val is None: instead of if val == None:?

In Python, None is a unique singleton object in memory. The == operator calls the object’s __eq__() magic method, which can be overridden by a class to return unexpected results. The is operator tests identity (whether both operands reference the exact same memory address using id()), which is faster, safer, and idiomatic Python.

Q4: Should I use snake_case for everything in Python?

No. Use snake_case for variables, function names, and method names. Use PascalCase for classes and custom exceptions, and use SCREAMING_SNAKE_CASE for global module-level constants.

Q5: Can I use PEP 8 in Jupyter Notebooks and Google Colab?

Yes! In Jupyter Notebooks, you can format cells using Ruff or Black with the nbqa library (pip install nbqa), or install the JupyterLab code formatter extension. Clean formatting in data science notebooks is essential for reproducible research.

Q6: Does PEP 8 mandate static type hints?

PEP 8 does not strictly require type hints (type annotations were later introduced in PEP 484). However, modern PEP 8 guidelines include spacing rules for type hints: use a space after the colon, but not before (name: str = "Sumit").


Mastering PEP 8 is one of the most rewarding milestones in your programming journey. Writing clean code builds discipline, improves problem-solving clarity, and sets you apart in every coding viva and technical interview.

Continue expanding your Python expertise with our structured resources:

Tags:#Python#PEP 8#Clean Code#Python Style Guide#Coding Standards#Beginner Guide#MSK Tutorials
Recommended Training Programs

Master These Skills in Our Offline Labs

Beginner1 MONTHS

Python for Beginners

Master Python programming from scratch in 1 month. Comprehensive hands-on curriculum covering variables, data types, numbers, strings, operators, data structures (lists, tuples, sets, dicts), control flow, loops, functions, file handling, and 4 real-world projects.

Explore Course Curriculum
Beginner to Advanced3 MONTHS

Python Mastery (Beginner to Advanced) - 3 Months

Master Python programming from zero fundamentals to enterprise software engineering in 3 months. This comprehensive 3-in-1 Grand Mastery Combo bundles Python for Beginners, Python for Intermediate, and Python for Advanced — spanning 45 chapters, 218 structured topics, 1,099 interactive quiz questions, and 14 production portfolio projects across OOP, multithreading, asyncio, networking, and databases.

Explore Course Curriculum