Stop Writing Python Functions the Wrong Way! Do This Instead!

Avoid common mistakes and learn best practices to make your Python functions more efficient and readable.

Stop Writing Python Functions the Wrong Way! Do This Instead!
Photo by Nadine E on Unsplash

WRITE CLEANER, SMARTER PYTHON FUNCTIONS!

Stop Writing Python Functions the Wrong Way! Do This Instead!

Python is a fantastic language — clean, expressive, and easy to learn.

But let’s be honest: many developers (even experienced ones) make subtle mistakes when writing functions. These mistakes might not break your code immediately, but they can lead to hard-to-maintain, inefficient, or confusing code over time.
If you’ve been writing Python functions like it’s just about getting things to work, it’s time to level up. Let’s explore common pitfalls and how to fix them with best practices.

1. Stop Using Default Mutable Arguments!

The Wrong Way:

Python initializes default arguments only once at function definition, not every time the function is called. This means the same list is shared across multiple calls, leading to unexpected behavior.

def add_item(item, items=[]): 
    items.append(item) 
    return items 
 
print(add_item('apple'))  # ['apple'] 
print(add_item('banana')) # ['apple', 'banana'] ???

The Right Way:

Always use None as the default value for mutable arguments and initialize them inside the function.

def add_item(item, items=None): 
    if items is None: 
        items = [] 
    items.append(item) 
    return items 
 
print(add_item('apple'))  # ['apple'] 
print(add_item('banana')) # ['banana']

2. Don’t Return Different Data Types

The Wrong Way:

Returning inconsistent data types makes your function harder to work with and can lead to unexpected errors.

def process(value): 
    if value > 10: 
        return "Success" 
    else: 
        return 0  # Mixing str and int

The Right Way:

Using None (or a consistent type) makes the function predictable and easier to debug.

from typing import Optional 
 
def process(value: int) -> Optional[str]: 
    return "Success" if value > 10 else None

3. Stop Writing Bloated Functions — Keep Them Small

The Wrong Way:

This function does too much. It calculates price, applies tax, considers discounts, and includes shipping.

def calculate_price(quantity, price, tax_rate, discount, shipping): 
    total = (quantity * price) + shipping 
    total += total * tax_rate 
    if discount: 
        total -= total * discount 
    return total

The Right Way (Break it Down!):

Now, each function does one thing well — easier to test, debug, and reuse.

def calculate_subtotal(quantity, price): 
    return quantity * price 
 
def apply_tax(subtotal, tax_rate): 
    return subtotal + (subtotal * tax_rate) 
 
def apply_discount(amount, discount): 
    return amount - (amount * discount) 
 
def calculate_total(quantity, price, tax_rate, discount, shipping): 
    subtotal = calculate_subtotal(quantity, price) 
    taxed_total = apply_tax(subtotal, tax_rate) 
    discounted_total = apply_discount(taxed_total, discount) 
    return discounted_total + shipping

4. Use f-strings Instead of Old-School String Formatting

The Wrong Way:

def greet(name, age): 
    return "Hello, my name is %s and I am %d years old." % (name, age)

or

def greet(name, age): 
    return "Hello, my name is {} and I am {} years old.".format(name, age)

The Right Way (More Readable & Performant!)

def greet(name, age): 
    return f"Hello, my name is {name} and I am {age} years old."
f-strings are faster, more readable, and the preferred way to format strings in Python.

5. Utilize Type Hints for Clarity

The Wrong Way:

This works, but what type are a and b? Integers? Floats? Strings?

def add_numbers(a, b): 
    return a + b

The Right Way:

Type hints make functions self-documenting and prevent unintended behavior.

def add_numbers(a: int, b: int) -> int: 
    return a + b

6. Use enumerate() Instead of Manually Tracking Indexes

The Wrong Way:

fruits = ["apple", "banana", "cherry"] 
index = 0 
for fruit in fruits: 
    print(f"{index}: {fruit}") 
    index += 1

The Right Way:

enumerate() eliminates manual index tracking, making the loop cleaner and more Pythonic.

fruits = ["apple", "banana", "cherry"] 
for index, fruit in enumerate(fruits): 
    print(f"{index}: {fruit}")

7. Avoid try-except for Flow Control

The Wrong Way:

def get_price(data): 
    try: 
        return data["price"] 
    except KeyError: 
        return 0

The Right Way:

Using .get() is more readable and avoids unnecessary exception handling.

def get_price(data): 
    return data.get("price", 0)

Final Thoughts

Writing better Python functions isn’t just about making them work — it’s about making them readable, maintainable, and efficient. By avoiding these common mistakes and following best practices, you’ll write cleaner, more professional Python code.

Now that you know how to improve your Python functions, go back to your old code and refactor it! You’ll be surprised how much cleaner and more efficient it becomes.

Got any other function-writing tips? Drop them in the comments below!