Preparing your learning space...
100% through Rules to be Follow tutorials
Write Python code that follows a consistent set of rules. This guide covers the essential rules for code style, naming, PEP 8, and keeping your code clean — no fluff.
These rules aren't made up by some committee to annoy you. They exist because thousands of developers ran into the same problems and figured out what works. Following them means:
PEP 8 is the official style guide for Python code. It's not enforced by the Python interpreter — your code will run fine without it — but it's the closest thing Python has to a standard dialect.
The short version of PEP 8:
| What | Rule |
|---|---|
| Indentation | 4 spaces per level. No tabs. |
| Line length | Max 79 characters for code, 72 for comments |
| Blank lines | 2 blank lines around functions/classes, 1 around methods |
| Imports | One per line, grouped: stdlib → third-party → local |
| Spaces | Put spaces around operators and after commas |
PEP 8 is a guideline, not a law. If following it makes code less readable (like breaking a long line at an awkward spot), don't do it. But know the rule before you break it.
Python uses specific naming patterns. Follow them and other developers instantly understand what your names mean.
| Element | Convention | Example |
|---|---|---|
| Variable | snake_case | user_name, total_price |
| Function | snake_case | get_user(), calculate_total() |
| Class | PascalCase | UserProfile, ShoppingCart |
| Constant | UPPER_SNAKE_CASE | MAX_RETRIES, DEFAULT_TIMEOUT |
| Private (internal) | Leading underscore | _internal_helper(), _cache |
| Avoid conflict | Trailing underscore | class_, type_ |
Examples of good vs bad naming:
# Bad — what is this?
def func(x, y):
return x * y * 0.15
# Good — clear intent
def calculate_tax(price, tax_rate):
return price * tax_rate
# Bad — meaningless
a = ["Alice", "Bob", "Charlie"]
# Good — descriptive
student_names = ["Alice", "Bob", "Charlie"]
# Bad — confusing abbreviation
def calc_amt(itms):
return sum(i.prc for i in itms)
# Good — readable
def calculate_total(items):
return sum(item.price for item in items)
Boolean variables should read like yes/no questions:
# Good
is_active = True
has_permission = False
user_logged_in = True
# Avoid
active = True # unclear — active what?
flag = True # meaningless
status = False # what status?
Four spaces. Always. Configure your editor to insert spaces when you press Tab.
# Correct
if user.is_admin:
grant_access()
log_event("admin access")
else:
show_error("forbidden")
# Wrong (tabs, 2 spaces, or mixed)
if user.is_admin:
grant_access()
log_event("admin access")
Keep lines under 79 characters. When you need to break a long line:
# Implicit line continuation (inside brackets)
result = some_function(
argument_one, argument_two,
argument_three, argument_four
)
# Backslash for long statements (use sparingly)
if very_long_condition_that_wont_fit and \
another_condition_here:
do_something()
# Breaking long strings
message = (
"This is a very long string that needs to be "
"split across multiple lines for readability."
)
import os
import sys
# 2 blank lines above — PEP 8 says use 2 between top-level functions
def first_function():
pass
def second_function():
pass
class MyClass:
# 1 blank line above — PEP 8 says use 1 between methods
def method_one(self):
pass
def method_two(self):
pass
# Standard library — comes with Python, no pip install needed
import os
import sys
from datetime import datetime
# Third-party — installed separately via pip
import requests
from flask import Flask
# Local — your own project modules
from myapp.models import User
from myapp.utils import format_date
Group them. Put a blank line between groups. Don't mix them.
# Good
x = 5 + 3
items = [1, 2, 3]
result = (a + b) * c
# Bad
x=5+3
items = [1,2,3]
result = ( a + b ) * c
# Good — no space before colon, parenthesis
def func(arg1, arg2):
print(func(10, 20))
# Bad
def func( arg1, arg2 ) :
print( func( 10, 20 ) )
Write comments that explain why, not what. The code already says what it does.
# Bad — states the obvious
# Add one to counter
counter += 1
# Good — explains the reason
# Offset by 1 because the API uses 1-based indexing
counter += 1
# Bad — unclear
# Check the thing
if len(items) > 5:
process(items)
# Good — explains the purpose
# Batch processing: API rejects more than 5 items at once
if len(items) > 5:
process(items)
Docstrings for functions and classes that others will use:
def calculate_bmi(weight_kg, height_m):
"""Calculate Body Mass Index from weight (kg) and height (m)."""
return weight_kg / (height_m ** 2)
def fetch_user(user_id):
"""
Fetch a user from the database.
Args:
user_id: The user's unique identifier.
Returns:
User object if found, None otherwise.
"""
return database.get(User, user_id)
Not every tiny helper function needs a docstring. If the name is clear enough, skip it.
A function should have one job. If it's doing multiple things, split it.
# Bad — this function does 4 different things
def process_user_data(user):
user.save()
email = Email()
email.send_welcome(user.email)
create_default_settings(user)
log_action("user_created")
return True
# Good — split into separate functions, each has one job
def create_user(user):
user.save()
def notify_user(user):
email = Email()
email.send_welcome(user.email)
def init_user_settings(user):
create_default_settings(user)
Name your numbers. Nobody knows what 86400 means.
# Bad
if age > 65:
give_discount()
# Good
SENIOR_CITIZEN_AGE = 65
if age > SENIOR_CITIZEN_AGE:
give_discount()
# Bad
if user.role == 3:
delete_post(post_id)
# Good
ADMIN_ROLE_ID = 3
if user.role_id == ADMIN_ROLE_ID:
delete_post(post_id)
# Even better — use enums
from enum import Enum
class Role(Enum):
ADMIN = 3
EDITOR = 2
VIEWER = 1
if user.role == Role.ADMIN:
delete_post(post_id)
If you write the same logic twice, extract it.
# Bad — repeated logic
def process_order(order):
if order.total > 100:
discount = order.total * 0.1
order.total -= discount
def process_refund(order):
if order.total > 100:
discount = order.total * 0.1
order.total -= discount
# Good — one place
def apply_discount(order):
if order.total > 100:
discount = order.total * 0.1
order.total -= discount
def process_order(order):
apply_discount(order)
# ...
def process_refund(order):
apply_discount(order)
# ...
Too many nested levels means your function is too complex. Fix it with early returns or smaller functions.
# Bad — deeply nested
def process_user(user):
if user:
if user.is_active:
if user.has_permission:
do_something()
else:
handle_no_permission()
else:
handle_inactive()
else:
handle_none()
# Good — early returns avoid deep nesting
def process_user(user):
# Reject invalid cases immediately, then run the real logic
if not user:
handle_none()
return
if not user.is_active:
handle_inactive()
return
if not user.has_permission:
handle_no_permission()
return
do_something()
in for membership tests# Bad
if fruit == "apple" or fruit == "banana" or fruit == "orange":
print("valid fruit")
# Good
if fruit in ("apple", "banana", "orange"):
print("valid fruit")
not over negating the condition# Bad
if is_empty == True:
print("empty")
if is_empty == False:
print("not empty")
# Good
if is_empty:
print("empty")
if not is_empty:
print("not empty")
# Bad — file stays open if an error occurs before .close()
file = open("data.txt", "r")
data = file.read()
file.close()
# Good — with block closes the file even on errors
with open("data.txt", "r") as file:
data = file.read()
| Mistake | Why it's bad | Fix |
|---|---|---|
from module import * | Pollutes namespace, unclear where names come from | Import specific names |
| Mutable default args | def f(x=[]) shares one list across all calls | Use None and create inside |
Comparing to True/False | Redundant | if x: or if not x: |
Bare except: | Catches everything (including keyboard interrupt) | except Exception: |
Using is for value comparison | is checks identity, not equality | Use == for values |
Mutable default argument trap:
# Bad — appends to the SAME list every call
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — bug!
# Good
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
import os,sys
from datetime import *
def calc(x,y):
return x*y/100
class temp:
def __init__(self,n,a):
self.n=n
self.a=a
def show(self):
print(self.n,self.a)
def main():
u=temp("Alice",25)
u.show()
r=calc(200,15)
print("Result:",r)
if __name__=="__main__":
main()
from datetime import datetime
# Only import what you use. os and sys were removed — not needed here.
def calculate_percentage(value, percent):
"""Return `percent`% of `value`."""
return value * percent / 100
class User:
"""Represents a basic user profile."""
def __init__(self, name, age):
self.name = name
self.age = age
def display(self):
print(f"{self.name} is {self.age} years old")
def main():
user = User("Alice", 25)
user.display()
result = calculate_percentage(200, 15)
print(f"15% of 200 is {result}")
if __name__ == "__main__":
main()
Save your progress and earn XP for completing tutorials.
Keep learning
Technology
Python
Lesson group
Rules to be Follow
Progress
100% complete