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
362: Optimizing Hot Loops
Imagine you're a professional chef prepping for a massive banquet. You have to dice 500 carrots. If you walk all the way to the walk-in cooler, grab one single carrot, walk back to your station, dice it, and then walk back to the cooler for the next one, you're going to be there all night. You aren't spending your time dicing; you're spending your time walking.
In Python, a "hot loop" is that dicing station. It's the part of your code that runs millions of times. When a loop is "hot," the tiny inefficiencies that you normally ignore—like looking up a variable or calling a method—become massive bottlenecks. To optimize them, we have to stop "walking to the cooler" inside the loop and bring everything we need to the counter before we start.
The Hidden Cost of the Dot
Every time you use a dot in Python (like my_list.append()), Python has to perform a lookup. It asks, "Does my_list have an attribute called append?" If you do this ten million times, those lookups add up to a significant amount of wasted time. I've seen production scripts shave off seconds just by caching a method to a local variable.
# The slow way: looking up .append every iteration
results = []
for i in range(10_000_000):
results.append(i * 2)
# The faster way: caching the method locally
results = []
append_func = results.append
for i in range(10_000_000):
append_func(i * 2)
By assigning results.append to append_func, we've moved the lookup outside the loop. Now, the loop just calls a local variable, which is much faster in the Python virtual machine.
Stop Recalculating the Constants
This is the most common mistake I see. Someone will put a calculation inside a loop that produces the same result every single time. It seems innocent, but it's a silent killer.
# Bad: calculating the radian conversion every time
import math
points = [1, 2, 3, 4, 5] # Imagine this is 1 million points
scaled_points = []
for p in points:
scaled_points.append(p * (math.pi / 180))
# Good: move the invariant outside
conversion_factor = math.pi / 180
for p in points:
scaled_points.append(p * conversion_factor)
That division math.pi / 180 doesn't change based on p. Moving it outside means we do the math once instead of a million times. It's a small change, but in a hot loop, it's the difference between a snappy app and one that feels frozen.
Letting C Do the Heavy Lifting
Python is an interpreted language, which means it's flexible but slow. However, many of Python's built-in functions are actually written in C. When you use a list comprehension or a built-in like sum() or map(), you're essentially handing the work over to a highly optimized C engine.
If you're manually looping through a list to add numbers or filter items, you're doing it the "slow" way. Try to push as much logic as possible into these built-ins. A list comprehension isn't just syntactic sugar; it's generally faster than a for loop with .append() because it optimizes the way the list is constructed internally.
# Slow: Manual loop
squares = []
for x in range(10_000_000):
squares.append(x * x)
# Fast: List comprehension
squares = [x * x for x in range(10_000_000)]
I'll be honest: you shouldn't obsess over this for every single loop in your program. Most of your code isn't "hot." But once you identify that one function that's eating 80% of your CPU time, these are the first levers you should pull.
📋 Practical Task
Optimizing a High-Volume Coordinate Transformation Loop
You have a function that transforms a massive list of (x, y) coordinates by multiplying them by a scaling factor and shifting them by an offset. The current implementation is incredibly slow because it's doing too much work inside the loop.
Your Task: Rewrite the transform_coordinates function to optimize the hot loop. You must apply at least three techniques discussed in the lesson:
- Move invariant calculations outside the loop.
- Cache the list
appendmethod to a local variable. - Convert the final logic into a list comprehension for maximum speed.
import math
def transform_coordinates(coords, scale_factor):
# This is the "hot" function
transformed = []
for x, y in coords:
# Problem: The offset is recalculated every single iteration
# Problem: .append is looked up every iteration
offset = math.sqrt(scale_factor) * 1.5
transformed.append((x * scale_factor + offset, y * scale_factor + offset))
return transformed
# Test data: 1 million coordinates
test_coords = [(i, i) for i in range(1_000_000)]
# Your optimized version should be significantly faster
result = transform_coordinates(test_coords, 2.5)
There are no comments for now.