Skip to Content
Course content

80: Type Hints for Function Signatures

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're walking into a professional kitchen. You see a prep station with five different stainless steel bowls. In one, there's a white powder. In another, a clear liquid. If the chef didn't label those bowls, you'd have to taste them—or guess—before you knew if you were grabbing salt, sugar, water, or white vinegar. You might get it right most of the time, but the one time you mistake salt for sugar, the whole dessert is ruined.

Type hints in Python are those labels on the bowls. For years, Python has been "dynamically typed," meaning it doesn't care what's in the bowl until the very moment it tries to use it. Type hints don't change how the code runs, but they tell you (and your tools) exactly what's supposed to be there so you don't have to play the guessing game.

Stopping the guessing game

Let's look at a function I might have written in a rush a few years ago. Suppose we're building a system to calculate the final price of an item after a discount:

def apply_discount(price, discount):
    return price * (1 - discount)

If you're the one calling this function, you have to guess. Does price need to be an integer or a float? Does discount need to be a percentage (like 20) or a decimal (like 0.2)? If you pass in "20" as a string by mistake, Python won't complain until the code actually hits that multiplication line and crashes. I've spent way too many hours debugging exactly this kind of thing.

By adding type hints, we turn that mystery into a contract:

def apply_discount(price: float, discount: float) -> float:
    return price * (1 - discount)

Now, the : float tells you the inputs should be floating-point numbers, and the -> float tells you exactly what the function will hand back to you. It's an immediate signal to anyone reading the code: "Give me two decimals, and I'll give you one back."

Handling the tricky stuff

Basic types like int, str, and bool are easy. But in the real world, we're usually dealing with lists of things or dictionaries. For a while, you had to import these from a module called typing, but in modern Python (3.9+), you can just use the built-in collection types.

Say you're writing a function that takes a list of product prices and returns the average. You wouldn't just say it takes a list; you'd want to specify a list of floats:

def calculate_average_price(prices: list[float]) -> float:
    return sum(prices) / len(prices)

I also frequently run into situations where a function might return a value, or it might return None if something goes wrong. For that, we use Optional (or the newer pipe | syntax). Here is how I'd handle a user lookup:

def find_user_by_id(user_id: int) -> str | None:
    # Logic to find user...
    # Returns the username (str) if found, otherwise None
    return None

That str | None is a lifesaver. It forces the person calling the function to remember that they need to check if the result is None before they try to call a string method on it.

The "Gentle Suggestion" reality

Here is the most important thing to understand: Python is still Python. Type hints are not enforced by the Python interpreter at runtime. If you hint that a function takes an int but you pass it a str, Python will still try to run it. It won't stop the program from starting.

So why bother? Because tools like Mypy or the built-in checkers in VS Code and PyCharm use these hints to highlight errors in red before you ever press the "Run" button. It's the difference between catching a typo while you're writing and catching a crash while your customer is using the app.




📋 Practical Task

Refactoring the Order Management System

You've inherited a piece of code for a small e-commerce order system. The previous developer left no type hints, and the team is struggling to understand what data types are expected for the order processing logic. Your task is to add comprehensive type hints to the following functions.

Requirements:

  • Add type hints for all parameters.
  • Add return type hints for all functions.
  • Use list[float] for collections of prices.
  • Use the pipe | syntax for types that could be a specific type or None.
# Add type hints to these functions

def calculate_total(items: ..., tax_rate: ...) -> ...:
    return sum(items) * (1 + tax_rate)

def get_discount_code(email: ..., database: ...) -> ...:
    # Imagine this looks up a code in a dictionary. 
    # It returns a string if found, otherwise None.
    return database.get(email)

def apply_promo_code(total: ..., code: ...) -> ...:
    if code == "SAVE10":
        return total * 0.9
    return total
Rating
0 0

There are no comments for now.

to be the first to leave a comment.