

Photo by author
# Introduction
You have written your first The python The function does this. You run it, it produces the right output, and you feel that rush of success. Then you watch it again two weeks later and think: “What does that do?”
Writing readable Python functions isn’t about being smart or advanced. It’s about writing functions that communicate your intent clearly, manage your responsibilities neatly, and make the next person’s job easier.
Let’s learn to write functions that read like good prose, not cryptic puzzles that readers find hard to wrap their heads around.
🔗 You can find the code on GitHub.
# 1. Name your actions as if you were explaining to a friend
Function names are the first thing people read. A good name tells you exactly what the function does without you having to read the code inside.
Bad example:
def proc(d):
return sum(d) / len(d)Good example:
def calculate_average(numbers):
return sum(numbers) / len(numbers)Function name calculate_average uses a function (“calculate”) that tells you that it performs an action, followed by a calculation (“average”). The parameter number explicitly indicates that it expects a collection of numeric values.
The function uses the body sum(numbers) and to include all values len(numbers) To count them, then divide to get the average. Anyone reading this code immediately understands what it does without the need for comments or documentation.
# 2. Use descriptive parameter names
Single-character variables can save typing, but they come at a cost of understanding. Parameters are the inputs to your function, so be clear about what you expect.
Bad example:
def discount(p, r):
return p * (1 - r)Good example:
def apply_discount(original_price, discount_rate):
return original_price * (1 - discount_rate)Anyone reading now apply_discount(100, 0.2) Assumes you are taking 20% ​​from 100. Code is self-documenting. You don’t need to check the function definition to understand what arguments are passed.
# 3. Keep functions short and focused
Functions should do one job well. If your function has multiple responsibilities, it becomes difficult to test, reuse, and understand. So, break complex logic into smaller, focused functions.
Bad example:
def process_order(items, customer_email, discount_code):
# Calculate total
subtotal = sum(item("price") * item("quantity") for item in items)
# Apply discount
if discount_code == "SAVE10":
discount = 0.10
elif discount_code == "SAVE20":
discount = 0.20
else:
discount = 0
total = subtotal * (1 - discount)
# Send email
subject = f"Order Confirmation"
body = f"Your order total is ${total:.2f}"
send_email(customer_email, subject, body)
return totalGood example:
def calculate_order_subtotal(items):
return sum(item("price") * item("quantity") for item in items)
def get_discount_rate(discount_code):
discount_rates = {"SAVE10": 0.10, "SAVE20": 0.20}
return discount_rates.get(discount_code, 0)
def apply_discount_to_subtotal(subtotal, discount_rate):
return subtotal * (1 - discount_rate)
def send_order_confirmation_email(customer_email, total):
subject = "Order Confirmation"
body = f"Your order total is ${total:.2f}"
send_email(customer_email, subject, body)
def process_order(items, customer_email, discount_code):
subtotal = calculate_order_subtotal(items)
discount_rate = get_discount_rate(discount_code)
total = apply_discount_to_subtotal(subtotal, discount_rate)
send_order_confirmation_email(customer_email, total)
return totalEach function now has a single, clear purpose. important process_order The function reads like an instruction: calculate subtotal, get discount, apply it, send email, return total.
# 4. Add documentation to explain the purpose
Function names tell you what a function does, but docstrings explain why it exists, what it expects, and what it returns. This is especially helpful for complex logic or ambiguous behavior.
Good example:
def calculate_shipping_cost(weight_kg, distance_km, is_express=False):
"""
Calculate shipping cost based on package weight and distance.
Args:
weight_kg (float): Package weight in kilograms
distance_km (float): Shipping distance in kilometers
is_express (bool): Whether to use express shipping (default: False)
Returns:
float: Total shipping cost in dollars
Example:
>>> calculate_shipping_cost(5.0, 100, is_express=True)
45.50
"""
base_rate = 2.50
per_kg_rate = 1.20
per_km_rate = 0.15
express_multiplier = 2.0 if is_express else 1.0
cost = (
base_rate + (weight_kg * per_kg_rate) + (distance_km * per_km_rate)
) * express_multiplier
return round(cost, 2)The docstring explains what each parameter means, what type it should be, and what the function returns. Anyone using this function knows exactly how to call it without reading the implementation.
# 5. Use clear variable names within functions
Like parameters, internal variables must also be defined. Don’t make people guess or solve acronyms tmp or x represents
Bad example:
def calc_bmi(w, h):
h_m = h / 100
res = w / (h_m**2)
return round(res, 1)Good example:
def calculate_bmi(weight_kg, height_cm):
height_meters = height_cm / 100
bmi = weight_kg / (height_meters**2)
return round(bmi, 1)variable height_meters Tells you exactly what change has occurred. And as seen, variable bmi Calculates body mass index (BMI).
# 6. Avoid magic numbers; Use named constants instead
The numbers scattered through your code are “magic”, meaning their purpose is unclear. Give them meaningful names so that readers understand their importance.
Bad example:
def calculate_late_fee(days_overdue):
if days_overdue <= 7:
return days_overdue * 2
else:
return 14 + (days_overdue - 7) * 5Good example:
def calculate_late_fee(days_overdue):
DAILY_FEE_FIRST_WEEK = 2
GRACE_PERIOD_DAYS = 7
BASE_FEE_AFTER_GRACE = 14
DAILY_FEE_AFTER_GRACE = 5
if days_overdue <= GRACE_PERIOD_DAYS:
return days_overdue * DAILY_FEE_FIRST_WEEK
else:
days_after_grace = days_overdue - GRACE_PERIOD_DAYS
return BASE_FEE_AFTER_GRACE + (days_after_grace * DAILY_FEE_AFTER_GRACE)Now the fee structure is clear. Constants document your business rules. When rates change, you update a clearly named price instead of searching for mysterious numbers.
# 7. Use type notation for clarification
Type hints to readers What types does your function expect and return?. This avoids confusion and catches bugs quickly. It’s good practice to add type hints to your functions.
Good example:
def format_user_greeting(user_name: str, age: int, is_member: bool = False) -> str:
membership_status = "member" if is_member else "guest"
return f"Hello {user_name}, age {age}. You are a {membership_status}."Type indicators clarify: user_name is a string, age is a number, is_member is a boolean with default Falseand the function returns a string. Your integrated development environments (IDEs) can use this information to better autocompile and error check.
# The result
Writing readable functions is not difficult. They just need to think about your customer. Every choice you make — name, composition, comments — either helps or hinders comprehension.
The goal is not perfect code. This is code that communicates clearly. Code that makes the next person say “Ah, I get it” instead of “What’s it actually doing?” It’s readable code, and you can write it from day one.
In the next article, we will learn how to write clean Python classes. Until then, keep coding!
Bala Priya c is a developer and technical writer from India. She loves working at the intersection of mathematics, programming, data science, and content creation. His areas of interest and expertise include devops, data science, and natural language processing. She enjoys reading, writing, coding and coffee! Currently, she is working on learning and sharing her knowledge with the developer community by authoring tutorials, how-to guides, opinion pieces and more. Bala also engages resource reviews and coding lessons.