Definition of a PEP in Python
A PEP (acronym for Python Enhancement Proposal) is an official design document that describes a new feature, a process, or a convention for the Python language. PEPs are the primary mechanism through which the Python community proposes, discusses, and adopts changes to the language.
Each PEP is identified by a unique number (for example, PEP 8, PEP 20, PEP 484) and follows a well-defined lifecycle: draft, accepted, rejected, or superseded. If you want to master Python in depth, understanding PEPs is essential, and this is a topic covered in our complete Python course.
PEPs are not code: they are written documents that guide the evolution of Python. They are aimed at Python core developers, but every Python developer benefits from reading them.
The Origin and Role of PEPs
The PEP system was introduced in the year 2000, inspired by the RFCs (Request for Comments) used for Internet standards and the DEPs (Design Enhancement Proposals) from other languages. The very first PEP, PEP 1, describes the PEP process itself.
PEPs fulfill several fundamental roles:
- Proposing new features: any major change to the language must go through a PEP before being implemented.
- Documenting design decisions: PEPs explain the why behind each technical choice.
- Establishing conventions: some PEPs define style standards and best practices (like the famous PEP 8).
- Informing the community: informational PEPs describe concepts or processes without necessarily proposing a change.
The Three Types of PEPs
Not all PEPs are identical. They fall into three distinct categories:
| PEP Type | Description | Example |
|---|---|---|
| Standards Track | Proposes a new feature or a change in the implementation of Python | PEP 484 (Type Hints) |
| Informational | Describes a concept, a convention, or provides general information | PEP 20 (The Zen of Python) |
| Process | Describes a process related to the Python ecosystem or proposes a procedural change | PEP 1 (PEP Purpose and Guidelines) |
The Lifecycle of a PEP
A PEP follows a precise path from its creation to its adoption (or rejection). Here are the main stages:
- Draft: the author writes the proposal and submits it to the community.
- Discussion: the PEP is debated on Python mailing lists and official forums.
- Accepted: if consensus is reached, the PEP is accepted by the Python Steering Council.
- Final: the feature is implemented and integrated into a Python release.
- Rejected: if the proposal does not convince, it is rejected.
- Superseded: a newer PEP may replace an older one.
Historically, it was Guido van Rossum (the creator of Python) who decided on the acceptance of PEPs as the BDFL (Benevolent Dictator for Life). Since 2019, it is the Steering Council elected by the community that makes these decisions.
The Most Important PEPs to Know
Among the hundreds of existing PEPs, some are absolutely essential for every Python developer. Let's review them.
PEP 8 — Style Guide for Python Code
PEP 8 is arguably the most well-known and most cited PEP. It defines the style conventions for writing readable and consistent Python code. Here are some iconic PEP 8 rules:
# ✅ PEP 8 compliant
def calculate_total_price(unit_price, quantity):
"""Calculate the total price of an order."""
total_price = unit_price * quantity
return total_price
# Two blank lines between module-level functions
def apply_discount(price, percentage=10):
"""Apply a discount to the given price."""
discount = price * percentage / 100
return price - discount
# ❌ Not PEP 8 compliant
def calculateTotalPrice(UnitPrice,Quantity):
totalPrice=UnitPrice*Quantity
return totalPriceThe main PEP 8 rules include:
- Indentation with 4 spaces (no tabs).
- Lines should not exceed 79 characters.
- Variable and function names use
snake_case. - class names use
PascalCase. - Two blank lines separate function and class definitions at the module level.
- Imports are placed at the top of the file, grouped by category.
PEP 20 — The Zen of Python
PEP 20 contains the 19 aphorisms that form the philosophy of Python. You can display them directly in the Python interpreter:
import thisHere are some of the most important principles:
# "Beautiful is better than ugly."
# Prefer elegant code
even_numbers = [x for x in range(20) if x % 2 == 0]
# "Explicit is better than implicit."
# Be explicit in your intentions
def convert_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5 / 9
# "Simple is better than complex."
# Favor simplicity
# Instead of:
result = (lambda x: x ** 2)(5)
# Prefer:
def square(x):
return x ** 2
result = square(5)
# "Readability counts."
# Readability is paramount
# Use descriptive names and docstringsThe Zen of Python guides the language's design decisions and serves as an excellent reference for writing Pythonic code. comment and docstring contribute to this philosophy of readability.
PEP 257 — Docstring Conventions
PEP 257 complements PEP 8 by detailing the conventions for writing docstring. It distinguishes between one-line docstrings and multi-line docstrings:
# One-line docstring (PEP 257)
def double(n):
"""Return the double of n."""
return n * 2
# Multi-line docstring (PEP 257)
def search_user(name, database):
"""Search for a user by name in the database.
Args:
name: The name of the user to search for.
database: The database instance.
Returns:
A dictionary containing the user's information,
or None if the user is not found.
Raises:
ConnectionError: If the database connection fails.
"""
pass
PEP 484 — Type Hints
PEP 484 introduced type annotations (type hints) in Python 3.5, profoundly transforming the way we write Python code:
from typing import Optional
def greet(name: str, enthusiastic: bool = False) -> str:
"""Generate a greeting message."""
if enthusiastic:
return f"Hello {name}!!!"
return f"Hello {name}"
def find_element(elements: list[str], target: str) -> Optional[int]:
"""Return the index of the target or None."""
try:
return elements.index(target)
except ValueError:
return None
# Type hints don't affect execution,
# but help IDEs and static analysis tools
result: str = greet("Alice", enthusiastic=True)
print(result) # Hello Alice!!!Type annotations work perfectly with f-string, list, dict, tuple, and set.
PEP 572 — The Walrus Operator (:=)
PEP 572, introduced in Python 3.8, added the assignment expression operator, nicknamed the "walrus operator" due to its resemblance to a walrus seen from the side :=:
# Without the walrus operator
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filtered = []
for n in numbers:
squared = n ** 2
if squared > 50:
filtered.append(squared)
print(filtered) # [64, 81, 100]
# With the walrus operator (PEP 572)
filtered = [squared for n in numbers if (squared := n ** 2) > 50]
print(filtered) # [64, 81, 100]
# Also useful to avoid redundant function calls
import re
text = "My email is [email protected]"
if (match := re.search(r'[\w.]+@[\w.]+', text)):
print(f"Email found: {match.group()}")
PEP 557 — Data Classes
PEP 557 introduced dataclass in Python 3.7, significantly simplifying the creation of classes primarily intended to store data:
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: float
quantity: int = 0
@property
def stock_value(self) -> float:
return self.price * self.quantity
# No need to define __init__, __repr__, __eq__
product = Product("Keyboard", 49.99, 25)
print(product) # Product(name='Keyboard', price=49.99, quantity=25)
print(product.stock_value) # 1249.75
Other Notable PEPs
Beyond the PEPs presented above, here are other PEPs that have marked the history of Python:
| PEP | Python Version | Feature Introduced |
|---|---|---|
| PEP 202 | 2.0 | List comprehensions |
| PEP 274 | 2.7 | Dict comprehensions |
| PEP 289 | 2.4 | Generator expressions (related to yield) |
| PEP 318 | 2.4 | Function decorators |
| PEP 343 | 2.5 | The with statement (context managers) |
| PEP 405 | 3.3 | Python virtual environments (venv) |
| PEP 498 | 3.6 | f-string (formatted strings) |
| PEP 618 | 3.10 | strict parameter for zip |
| PEP 634 | 3.10 | Structural Pattern Matching (match/case) |
How to Read and Find a PEP
All PEPs are publicly accessible on the official website peps.python.org. Here's how to navigate this repository efficiently:
# You can also access the Zen of Python (PEP 20)
# directly from your interpreter
import this
# To check the Python version and associated PEPs
import sys
print(sys.version)
# Example: 3.12.0 (main, Oct 2 2023, ...)
# Each Python version is itself associated with a PEP
# Python 3.12 → PEP 693
# Python 3.11 → PEP 664
# Python 3.10 → PEP 619Tip: each PEP follows a standardized format with a header (metadata), an abstract, a motivation, a detailed specification, and often a "Rejected Ideas" section that explains the alternatives that were discarded.
Best Practices Related to PEPs
Knowing PEPs is not enough: you need to apply them in your daily work. Here are the best practices we recommend:
- Follow PEP 8: use a linter like
flake8or a formatter likeblackto automatically check your code's compliance. - Use type hints (PEP 484): even though Python is a dynamically typed language, type annotations improve readability and enable error detection with
mypy. - Write docstrings (PEP 257): document your functions, classes, and modules following established conventions.
- Follow the Zen of Python (PEP 20): when in doubt about the best way to write code, refer to the fundamental principles.
- Stay informed: read the PEPs associated with new Python versions to understand the language's evolution.
# Example of code following multiple PEPs at once
from dataclasses import dataclass # PEP 557
@dataclass # PEP 557 - Data Classes
class Student:
"""Represents a student enrolled in a course.""" # PEP 257
name: str # PEP 484 - Type Hints
age: int # PEP 484
average: float # PEP 484
def is_passing(self) -> bool: # PEP 484 + PEP 8 (snake_case)
"""Check if the student is passing (average >= 10).""" # PEP 257
return self.average >= 10.0
# PEP 8: two blank lines before module-level definitions
def display_results(students: list[Student]) -> None:
"""Display the results of all students."""
for student in students:
status = "Passing" if student.is_passing() else "Failed"
# PEP 498 - f-strings
print(f"{student.name} ({student.age} years old): {status}")
# Usage
students = [
Student("Alice", 22, 15.5),
Student("Bob", 20, 8.0),
Student("Charlie", 21, 12.3),
]
display_results(students)
Writing Your Own PEP
Although it is an advanced process, any member of the Python community can propose a PEP. Here are the general steps:
- Discuss your idea first on the
discuss.python.orgforum to gather feedback. - Find a sponsor: a core developer must champion your proposal.
- Write the PEP in reStructuredText format following the template from PEP 12.
- Submit a pull request on the
python/pepsGitHub repository. - Iterate based on feedback from the community and the Steering Council.
Writing a PEP is a demanding exercise that requires solid knowledge of the Python language, its CPython implementation, and the community ecosystem. Most PEPs are written by core developers or very experienced contributors.
Frequently Asked Questions
What is the difference between a PEP and the official Python documentation?
A PEP is a proposal and design document: it explains why and how a feature should be added to the language. The official documentation, on the other hand, describes how to use already implemented features. In other words, the PEP precedes the documentation: a feature is first proposed via a PEP, then once accepted and implemented, it is documented in the official documentation.
Do I need to read all PEPs to be a good Python developer?
No, it is absolutely not necessary to read the hundreds of existing PEPs. Focus on the most important ones: PEP 8 (style guide), PEP 20 (Zen of Python), PEP 257 (docstrings), and PEP 484 (type hints). Then, you can consult specific PEPs related to the features you use on a daily basis.
Is PEP 8 mandatory?
PEP 8 is not technically mandatory: Python does not prevent the execution of non-compliant code. However, it is considered a de facto standard in the Python community. Most open-source projects, companies, and development teams require PEP 8 compliance. Tools like black, flake8, and pylint allow you to automatically check and apply these conventions.
How can I learn Python and understand PEPs in practice?
The best way to learn Python and integrate best practices from PEPs is to follow a structured learning path. We recommend our dedicated Python course on Believemy, which will guide you step by step in mastering the language while applying the conventions and best practices from the most important PEPs.