Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
80: Type Hints for Function Signatures
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 orNone.
# 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 totalThere are no comments for now.