Goal: Write functions that are easy to read, test, and reuse.
Rule 1: Name = Action
A function name is a verb plus an object. If you have to read the body to understand its purpose — the name is bad.
# Bad
def f(x, y): ...
def process(): ...
# Good
def calculate_total_price(base_price, quantity, tax_rate): ...
def validate_email(email): ...
Boolean functions start with is_, has_, can_:
def is_adult(age): return age >= 18
def has_permission(user): return user.role == "admin"
def can_withdraw(balance, amount): return balance >= amount
Rule 2: One Job (SRP)
# Bad: one function does everything
def process_user(name, age):
if age < 18: return False
with open("users.txt", "a") as f: f.write(f"{name},{age}\n")
send_email(name)
save_to_db(name, age)
return True
# Good: each function does one thing
def is_adult(age): return age >= 18
def save_user_to_file(name, age): ...
def send_welcome_email(name): ...
def register_user(name, age):
"""Register a new user."""
if not is_adult(age): return False
save_user_to_file(name, age)
send_welcome_email(name)
return True
Rule 3: Return Results, Don’t Print
A pure function returns a value and doesn’t change external state.
# Bad: calculation and output mixed
def calculate_discount(price, pct):
result = price * (1 - pct / 100)
print(f"Total: {result}")
return result
# Good: logic is separated from display
def calculate_discount(price, discount_percent):
"""Calculate the discounted price."""
return price * (1 - discount_percent / 100)
def show_discount_info(price, discount_percent):
final = calculate_discount(price, discount_percent)
print(f"Price: {price} → {final}")
Rule 4: Descriptive Parameters
# Bad
def f(x, y, z): return x + y * z
# Good — named arguments at the call site
def calculate_total_price(base_price, quantity, tax_rate):
"""Calculate the total cost including tax."""
return base_price + quantity * tax_rate
result = calculate_total_price(base_price=10, quantity=5, tax_rate=2)
More than 3–4 parameters — use a dict or dataclass.
Rule 5: Don’t Mutate Arguments
# Bad: modifies the passed list
def add_item(items, new_item):
items.append(new_item) # side effect!
# Good: returns a new list
def add_item(items, new_item):
"""Return a new list with the added item."""
return items + [new_item]
Rule 6: Default Values
def create_account(name, balance=0, currency="USD"):
"""Create a bank account."""
return {"name": name, "balance": balance, "currency": currency}
account1 = create_account("Alice")
account2 = create_account("Bob", 1000)
account3 = create_account("Charlie", 500, "EUR")
Rule 7: Document Functions
# Bad
def calc(p, d): return p - (p * d / 100)
# Good
def calculate_discounted_price(price, discount_percent):
"""
Calculate the price after applying a discount.
Args:
price: The original item price
discount_percent: Discount percentage (0-100)
Returns:
Price after the discount is applied
Example:
>>> calculate_discounted_price(1000, 20)
800.0
"""
return price * (1 - discount_percent / 100)
Rule 8: Keep Functions Short
A function should fit on one screen (20–30 lines). If it’s longer — split it.
def process_order(order_id):
"""Process the order end-to-end."""
order = validate_order(order_id)
if not check_inventory(order): return False
save_order_to_database(order)
send_confirmation_email(order)
return True
Good Function Checklist
- Name says WHAT it does (verb + object)
- One job (SRP)
- Returns a result, doesn’t print
- Descriptive parameter names
- Doesn’t mutate arguments
- Has a docstring
- Up to 20–30 lines
- Maximum 3–4 parameters
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!