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
156: Organizing Code into Modules
Up until now, we've been writing our code in single files. That works fine for a script that does one thing, but as soon as your project grows, your file becomes a "junk drawer." You spend more time scrolling up and down looking for a specific function than you do actually writing code. This is where modules come in.
Think of a module as just a Python file that contains functions, classes, or variables that you want to use in other parts of your program. It's about separation of concerns: one file handles the "how" (the logic), and another file handles the "when" (the execution).
Defining our conversion logic
I'm going to build a small utility that handles different types of conversions—temperature and currency. Instead of putting everything in one place, I'll create a dedicated file called converters.py. This file isn't meant to "run" on its own; it's a toolbox for other scripts to use.
# converters.py
def celsius_to_fahrenheit(celsius):
return (celsius * 9/5) + 32
def fahrenheit_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5/9
def usd_to_eur(amount, exchange_rate=0.92):
return amount * exchange_rate
Simple enough. No fancy logic here, just pure utility functions. Now, I want to actually use these in a separate script, which I'll call app.py.
Pulling tools from the toolbox
Now I'll create app.py. This is where the user interaction happens. I have two main ways to import my module: I can import the whole thing, or just the specific functions I need. I personally prefer importing specific functions because it makes it obvious where a function came from when you're reading the code six months later.
# app.py
from converters import celsius_to_fahrenheit, usd_to_eur
temp_c = 25
temp_f = celsius_to_fahrenheit(temp_c)
print(f"{temp_c}°C is {temp_f}°F")
dollars = 100
euros = usd_to_eur(dollars)
print(f"${dollars} is approximately €{euros}")
The "Accidental Execution" mistake
Here is where I usually trip up when I'm moving fast. Let's say I wanted to test my converters.py file while I was writing it, so I added some print statements at the bottom of that file to make sure the math was right.
# converters.py (Modified)
def celsius_to_fahrenheit(celsius):
return (celsius * 9/5) + 32
# ... other functions ...
print("Testing Celsius to Fahrenheit: 25C should be 77F")
print(celsius_to_fahrenheit(25))
The problem? Now, every time I run app.py, those test prints from converters.py show up in my console. Why? Because when Python imports a module, it executes the entire file. It doesn't know that those print statements were just for my own temporary testing.
Using the main guard
To fix this, we use a special Python idiom: the if __name__ == "__main__": block. This tells Python: "Only run this code if this file is being executed directly. If this file is being imported as a module, ignore this part."
I'll move my tests into that block in converters.py:
# converters.py (Fixed)
def celsius_to_fahrenheit(celsius):
return (celsius * 9/5) + 32
def fahrenheit_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5/9
def usd_to_eur(amount, exchange_rate=0.92):
return amount * exchange_rate
if __name__ == "__main__":
# This only runs if I do 'python converters.py'
# It will NOT run if I do 'import converters' in app.py
print("Running internal module tests...")
print(f"Test: 25C -> {celsius_to_fahrenheit(25)}F")
Now app.py stays clean, but I still have a way to quickly verify my logic by running the module file directly. It's a small habit that saves a lot of debugging headaches in larger projects.
📋 Practical Task
Build a Modular Geometry Calculator
You need to create a small project consisting of two files to calculate areas of different shapes.
- Create a file named
geometry.py. In this file, define two functions:calculate_circle_area(radius)andcalculate_rectangle_area(length, width). - At the bottom of
geometry.py, implement anif __name__ == "__main__":block that prints the result of a circle with radius 5 and a rectangle with 10x5 dimensions. - Create a second file named
main.py. Import both functions from thegeometrymodule and use them to calculate the area of a circle with radius 12 and a rectangle with 7x3 dimensions. Print the results with descriptive labels. - Run
main.pyand verify that the internal tests fromgeometry.pydo not appear in the output.
There are no comments for now.