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
52: The range() Function in Depth
I see this happen all the time when I'm reviewing code from developers moving into Python: they treat range() as if it's a function that builds a list of numbers and hands it to them. It seems intuitive, right? If you call range(5), you expect a list like [0, 1, 2, 3, 4]. But that's not actually what's happening under the hood, and misunderstanding this can lead to some very confusing bugs when you start dealing with larger datasets.
"range() returns a list" vs. "range() is a lazy sequence"
If you think range() creates a list, try running this in your terminal: print(range(10)). You won't see a list of numbers. You'll see range(0, 10). That's because range() returns a range object.
Here is why this matters. Imagine you need to iterate over a billion numbers. If range() created a list, Python would try to allocate enough RAM to hold a billion integers. Your computer would likely freeze or crash. But try this:
# This executes instantly and uses almost no memory
huge_range = range(1000000000)
print(huge_range)
It doesn't crash. Why? Because it's "lazy." It doesn't store the numbers; it stores the formula for the numbers (the start, the stop, and the step). It only generates the next number when the loop actually asks for it. I always tell my mentees to think of it as a recipe, not the finished meal.
"The stop value is the last number" vs. "The stop value is an exclusive boundary"
Another common stumble is the "off-by-one" error. Newcomers often assume that range(1, 10) will give them numbers 1 through 10. It doesn't. It gives them 1 through 9.
In Python, the stop argument is exclusive. This feels annoying until you realize it makes working with list indices a breeze. Since Python lists are 0-indexed, a list with 5 elements has indices 0, 1, 2, 3, and 4. By making the stop value exclusive, range(len(my_list)) gives you exactly the indices you need without you having to subtract one manually.
# If we want to process exactly 5 items:
for i in range(5):
print(f"Processing item {i}")
# Prints 0, 1, 2, 3, 4. Total count: 5.
"range() only counts up" vs. "Using the step argument for flexibility"
You've probably used range(stop) or range(start, stop), but the third argument—the step—is where the real power is. Most people assume it's just for skipping numbers (like counting by twos), but you can also use it to walk backward.
The trick is that when you use a negative step, your start must be greater than your stop. If you try range(0, 10, -1), Python won't throw an error, but it will return an empty sequence because it's impossible to reach 10 from 0 by counting downward. I've spent way too many hours debugging "invisible" loops that didn't execute because the range logic was inverted.
# Counting down from 10 to 1
for i in range(10, 0, -1):
print(i)
# Note: This stops at 1 because 0 is exclusive.
Whether you are iterating through a list in reverse or skipping every other element in a dataset, the step argument is your primary tool. Just remember: the direction of the step must match the relationship between your start and stop values.
📋 Practical Task
The Alternating Index Sequence Generator
In a real-world scenario, you might need to process only specific indices of a list—for example, only the even-indexed elements for one operation and the odd-indexed elements for another.
Your Task:
Write a script that takes a list of strings called data_points. Use the range() function with a step argument to perform the following:
- Print all elements located at even indices (0, 2, 4...).
- Print all elements located at odd indices (1, 3, 5...).
- Finally, use
range()to print the indices of the list in reverse order, starting from the last element down to 0.
data_points = ["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta"]
# Your code here:
There are no comments for now.