Introduction
As a JavaScript developer transitioning to Python, you'll find that while the core concepts of loops remain similar, there are significant differences in syntax, behavior, and available loop constructs. This guide will help you understand how loops in Python differ from those in JavaScript, highlighting key similarities and differences to help you write idiomatic Python code.
The code for this lesson can be found in the /week2/day2/js_vs_python_loops.py file in your course repository.
For Loops: Fundamental Differences
The most significant difference you'll encounter is that Python's for loop is fundamentally different from JavaScript's. In Python, for loops are designed to iterate directly over sequences (like lists, strings, tuples) or other iterables, rather than incrementing a counter as in JavaScript.
JavaScript For Loop
In JavaScript, the traditional for loop has three parts: initialization, condition, and increment:
// JavaScript for loop
for (let i = 0; i < 5; i++) {
console.log(i); // Outputs 0, 1, 2, 3, 4
}
Python For Loop
In Python, the for loop directly iterates over a sequence:
# Python for loop
for i in range(5):
print(i) # Outputs 0, 1, 2, 3, 4
Notice that Python uses range(5) to create a sequence of numbers from 0 to 4. The Python for loop is actually more similar to JavaScript's for...of loop, which was introduced in ES6.
JavaScript for...of Loop
// JavaScript for...of loop (similar to Python's for loop)
const items = ["apple", "banana", "cherry"];
for (const item of items) {
console.log(item); // Outputs each item in the array
}
Python For Loop with List
# Python for loop with a list
items = ["apple", "banana", "cherry"]
for item in items:
print(item) # Outputs each item in the list
Key Differences
- Python's
forloop doesn't use the initialization-condition-increment structure - Python's
forloop is designed to iterate over collections directly - Python doesn't have a direct equivalent of JavaScript's traditional
forloop - Python uses
range()to create sequences of numbers for iteration
The range() Function vs JavaScript Counter Loops
In JavaScript, you often use counter-based loops for numeric iterations. In Python, the range() function serves this purpose by generating a sequence of numbers to iterate over.
JavaScript Counter Loop
// JavaScript counter loop
for (let i = 0; i < 10; i++) {
console.log(i);
}
// Starting from a different value
for (let i = 5; i < 15; i++) {
console.log(i);
}
// Using a different step size
for (let i = 0; i < 20; i += 2) {
console.log(i); // Even numbers 0 to 18
}
Python range() Function
# Python range() with one argument (stop value)
for i in range(10): # 0 to 9
print(i)
# Python range() with two arguments (start, stop)
for i in range(5, 15): # 5 to 14
print(i)
# Python range() with three arguments (start, stop, step)
for i in range(0, 20, 2): # Even numbers 0 to 18
print(i)
Important Differences
- The
range()function's stop value is exclusive (like JavaScript's condition) - The default start value for
range()is 0 if not specified - The default step size for
range()is 1 if not specified - In Python 3,
range()returns a generator-like object, not an actual list
Creating a List with range()
# Convert range to a list in Python
numbers = list(range(5))
print(numbers) # [0, 1, 2, 3, 4]
# JavaScript equivalent using Array.from()
// const numbers = Array.from({ length: 5 }, (_, i) => i);
// console.log(numbers); // [0, 1, 2, 3, 4]
For...in Loops: A Common Confusion
One of the most common confusions for JavaScript developers is the difference between JavaScript's for...in loop and Python's for...in loop. Despite the identical syntax, they behave quite differently.
JavaScript for...in Loop
In JavaScript, for...in loops iterate over the enumerable properties (keys) of an object:
// JavaScript for...in loop iterates over object properties (keys)
const person = { name: "Alice", age: 30, job: "Developer" };
for (const key in person) {
console.log(key + ": " + person[key]);
}
// Outputs:
// name: Alice
// age: 30
// job: Developer
// CAUTION: Using for...in with arrays in JavaScript is discouraged
const arr = ["apple", "banana", "cherry"];
for (const index in arr) {
console.log(index + ": " + arr[index]);
}
// Outputs:
// 0: apple
// 1: banana
// 2: cherry
Python for...in Loop
In Python, for...in loops iterate over the elements (values) of a sequence:
# Python for loop with a list (iterates over values)
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Outputs:
# apple
# banana
# cherry
# Python for loop with a dictionary (iterates over keys by default)
person = {"name": "Alice", "age": 30, "job": "Developer"}
for key in person:
print(key + ": " + str(person[key]))
# Outputs:
# name: Alice
# age: 30
# job: Developer
Critical Difference
The key difference to remember is:
- JavaScript's
for...in: Iterates over object property names (keys) - Python's
for...in: Iterates over collection values (elements)
In Python, when iterating over a dictionary with a simple for...in loop, you iterate over the keys, not the values. This behavior is similar to JavaScript's for...in, but for completely different reasons.
Dictionary Iteration: More Options in Python
Python provides more flexible ways to iterate over dictionaries than JavaScript does for objects.
JavaScript Object Iteration
// JavaScript object iteration
const person = { name: "Alice", age: 30, job: "Developer" };
// Iterating over keys (old way)
for (const key in person) {
console.log(key + ": " + person[key]);
}
// Modern alternatives for object iteration:
// Get keys as an array
console.log(Object.keys(person)); // ["name", "age", "job"]
// Get values as an array
console.log(Object.values(person)); // ["Alice", 30, "Developer"]
// Get key-value pairs as arrays
console.log(Object.entries(person)); // [["name", "Alice"], ["age", 30], ["job", "Developer"]]
// Using Object.entries() with for...of
for (const [key, value] of Object.entries(person)) {
console.log(key + ": " + value);
}
Python Dictionary Iteration
# Python dictionary iteration
person = {"name": "Alice", "age": 30, "job": "Developer"}
# Iterate over keys (default behavior)
for key in person:
print(key + ": " + str(person[key]))
# Explicit methods for dictionary iteration:
# Get all keys
print(person.keys()) # dict_keys(['name', 'age', 'job'])
# Get all values
print(person.values()) # dict_values(['Alice', 30, 'Developer'])
# Get key-value pairs as tuples
print(person.items()) # dict_items([('name', 'Alice'), ('age', 30), ('job', 'Developer')])
# Using .items() to get both key and value directly
for key, value in person.items():
print(key + ": " + str(value))
Key Differences
- Python's dictionary methods (
keys(),values(),items()) return view objects, not arrays - Python's
items()is similar to JavaScript'sObject.entries() - Python provides direct unpacking in the
forloop usingfor key, value in dict.items() - JavaScript requires destructuring assignment:
for (const [key, value] of Object.entries(obj))
While Loops: Mostly Similar
While loops are the construct with the most similarity between JavaScript and Python.
JavaScript While Loop
// JavaScript while loop
let count = 0;
while (count < 5) {
console.log(count);
count++;
}
// JavaScript do...while loop
let i = 0;
do {
console.log("This will run at least once: " + i);
i++;
} while (i < 3);
Python While Loop
# Python while loop
count = 0
while count < 5:
print(count)
count += 1
# Python doesn't have a do...while loop
# To simulate it, you need to structure the code differently:
i = 0
while True:
print("This will run at least once: " + str(i))
i += 1
if i >= 3:
break
Key Differences
- Python doesn't have a built-in
do...whileloop construct - Python uses
+=for increment (no++operator) - Python uses colons and indentation instead of curly braces
Loop Control: break, continue, else
Both JavaScript and Python provide break and continue for loop control, but Python adds an unusual else clause for loops.
JavaScript Loop Control
// JavaScript break
for (let i = 0; i < 10; i++) {
if (i === 5) {
break; // Exit the loop
}
console.log(i);
}
// JavaScript continue
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue; // Skip to the next iteration
}
console.log(i); // Only odd numbers
}
Python Loop Control
# Python break
for i in range(10):
if i == 5:
break # Exit the loop
print(i)
# Python continue
for i in range(10):
if i % 2 == 0:
continue # Skip to the next iteration
print(i) # Only odd numbers
Python's Unique "else" Clause for Loops
Python has a unique feature: you can add an else clause to loops. The else block executes after the loop completes normally (without a break).
# Python for loop with else
for i in range(5):
print(i)
else:
print("Loop completed successfully")
# The else clause won't execute if the loop is exited with a break
for i in range(5):
print(i)
if i == 2:
break
else:
print("This won't execute because the loop was broken")
This construct is especially useful for search loops:
# Finding an item in a list
def find_item(items, target):
for item in items:
if item == target:
print(f"Found {target}!")
break
else:
print(f"{target} not found in the list")
find_item([1, 2, 3, 4, 5], 3) # Found 3!
find_item([1, 2, 3, 4, 5], 6) # 6 not found in the list
There's no direct equivalent in JavaScript. You would typically use a flag variable or a conditional after the loop:
// JavaScript equivalent using a flag
function findItem(items, target) {
let found = false;
for (const item of items) {
if (item === target) {
console.log(`Found ${target}!`);
found = true;
break;
}
}
if (!found) {
console.log(`${target} not found in the list`);
}
}
List Comprehensions: Python's Powerful Alternative to map() and filter()
Python provides a powerful feature called "list comprehensions" that can often replace loops for creating lists. This is somewhat similar to JavaScript's map() and filter() methods, but with a more concise syntax.
JavaScript Array Transformations
// JavaScript map()
const numbers = [1, 2, 3, 4, 5];
const squared = numbers.map(num => num * num);
console.log(squared); // [1, 4, 9, 16, 25]
// JavaScript filter()
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
// Combining map() and filter()
const squaredEvens = numbers
.filter(num => num % 2 === 0)
.map(num => num * num);
console.log(squaredEvens); // [4, 16]
Python List Comprehensions
# Python list comprehension (like JavaScript's map())
numbers = [1, 2, 3, 4, 5]
squared = [num * num for num in numbers]
print(squared) # [1, 4, 9, 16, 25]
# Python list comprehension with condition (like JavaScript's filter())
evens = [num for num in numbers if num % 2 == 0]
print(evens) # [2, 4]
# Combining mapping and filtering in a single comprehension
squared_evens = [num * num for num in numbers if num % 2 == 0]
print(squared_evens) # [4, 16]
More Complex Comprehensions
Python comprehensions can be more powerful than they might initially seem:
# Nested loops in a comprehension
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Comprehensions for dictionaries (dict comprehensions)
squares_dict = {num: num * num for num in range(1, 6)}
print(squares_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Comprehensions for sets (set comprehensions)
letters = {char for char in "mississippi"}
print(letters) # {'m', 'i', 's', 'p'}
The JavaScript equivalent would be much more verbose:
// JavaScript equivalent for nested loops
const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const flattened = matrix.reduce((acc, row) => acc.concat(row), []);
console.log(flattened);
// JavaScript equivalent for dictionary comprehension
const squaresDict = Object.fromEntries(
Array.from({ length: 5 }, (_, i) => i + 1).map(num => [num, num * num])
);
console.log(squaresDict);
Iteration Protocols: Under the Hood
Both JavaScript and Python have underlying protocols for iteration, but they work differently.
JavaScript Iterator Protocol
// JavaScript custom iterable
const customIterable = {
data: [1, 2, 3, 4, 5],
// Define the iterator method
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { done: true };
}
}
};
}
};
// Now we can use our custom iterable in a for...of loop
for (const item of customIterable) {
console.log(item);
}
Python Iterator Protocol
# Python custom iterable
class CustomIterable:
def __init__(self):
self.data = [1, 2, 3, 4, 5]
# Define the __iter__ method
def __iter__(self):
self.index = 0
return self
# Define the __next__ method
def __next__(self):
if self.index < len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration # Signal end of iteration
# Now we can use our custom iterable in a for loop
custom_iterable = CustomIterable()
for item in custom_iterable:
print(item)
Key Differences
- Python uses
__iter__and__next__methods instead ofSymbol.iteratorandnext() - Python signals the end of iteration with a
StopIterationexception rather than adoneproperty - Python's iteration protocol is older and more established than JavaScript's
Performance Considerations and Best Practices
As you transition from JavaScript to Python, consider these performance tips and best practices for loops.
Common Performance Patterns
JavaScript
// Avoid array modification during iteration
const items = [1, 2, 3, 4, 5];
// Bad practice:
for (let i = 0; i < items.length; i++) {
if (items[i] % 2 === 0) {
items.splice(i, 1); // Modifies array during iteration
i--; // Adjust index
}
}
// Better approach:
const filtered = items.filter(item => item % 2 !== 0);
// Avoid expensive operations in loop conditions
// Bad practice:
for (let i = 0; i < someArray.length; i++) {
// length is accessed on each iteration
}
// Better approach:
const length = someArray.length;
for (let i = 0; i < length; i++) {
// length is accessed once
}
Python
# Avoid list modification during iteration
items = [1, 2, 3, 4, 5]
# Bad practice:
i = 0
while i < len(items):
if items[i] % 2 == 0:
items.pop(i) # Modifies list during iteration
else:
i += 1 # Only increment if we didn't remove
# Better approach:
items = [item for item in items if item % 2 != 0]
# or
filtered = [item for item in items if item % 2 != 0]
# Using built-in functions instead of loops
numbers = [1, 2, 3, 4, 5]
# Instead of manually summing:
total = 0
for num in numbers:
total += num
# Use the built-in sum() function:
total = sum(numbers)
Pythonic Loop Practices
Here are some idiomatic Python loop patterns that might be new to JavaScript developers:
# Enumerate for getting index and value together
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
# Zip for parallel iteration
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# Unpacking in loops
points = [(1, 2), (3, 4), (5, 6)]
for x, y in points:
print(f"Point: ({x}, {y})")
# Looping with default dictionary
from collections import defaultdict
word_counts = defaultdict(int)
for word in ["apple", "banana", "apple", "cherry"]:
word_counts[word] += 1
print(dict(word_counts)) # {'apple': 2, 'banana': 1, 'cherry': 1}
Common Pitfalls for JavaScript Developers
# Pitfall 1: Creating a list of functions that capture the loop variable
# In JavaScript, each closure captures its own copy of i due to let's block scope
functions = []
for i in range(5):
functions.append(lambda: i) # All functions capture the same i
for f in functions:
print(f()) # Prints 4, 4, 4, 4, 4
# Fix: Pass the value as a default argument
functions = []
for i in range(5):
functions.append(lambda i=i: i) # Each i is bound separately
for f in functions:
print(f()) # Prints 0, 1, 2, 3, 4
# Pitfall 2: Creating nested lists incorrectly
# This doesn't create independent inner lists
grid = [[0] * 3] * 3
grid[0][0] = 1
print(grid) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] - all rows changed!
# Fix: Use a list comprehension
grid = [[0 for _ in range(3)] for _ in range(3)]
grid[0][0] = 1
print(grid) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]] - only first row changed
Real-World Comparison Example
Let's look at a complete example implemented in both JavaScript and Python to see the differences in loop usage in a real-world scenario.
Task: Process Sales Data
We'll implement a function to process sales data, calculate statistics, and find top products.
JavaScript Implementation
// JavaScript version
function processSalesData(salesData) {
// Calculate total revenue
let totalRevenue = 0;
for (const sale of salesData) {
totalRevenue += sale.price * sale.quantity;
}
// Group sales by category
const categorySales = {};
for (const sale of salesData) {
const category = sale.category;
if (!categorySales[category]) {
categorySales[category] = 0;
}
categorySales[category] += sale.price * sale.quantity;
}
// Find top selling products
const productSales = {};
for (const sale of salesData) {
const product = sale.product;
if (!productSales[product]) {
productSales[product] = 0;
}
productSales[product] += sale.quantity;
}
// Convert to array and sort by quantity
const sortedProducts = Object.entries(productSales)
.map(([product, quantity]) => ({ product, quantity }))
.sort((a, b) => b.quantity - a.quantity);
const topProducts = sortedProducts.slice(0, 3);
// Calculate monthly sales
const monthlySales = {};
for (const sale of salesData) {
const month = new Date(sale.date).getMonth() + 1;
if (!monthlySales[month]) {
monthlySales[month] = 0;
}
monthlySales[month] += sale.price * sale.quantity;
}
return {
totalRevenue,
categorySales,
topProducts,
monthlySales
};
}
// Example usage
const salesData = [
{ product: "Laptop", category: "Electronics", price: 999.99, quantity: 3, date: "2023-01-15" },
{ product: "Smartphone", category: "Electronics", price: 699.99, quantity: 5, date: "2023-02-20" },
{ product: "Headphones", category: "Accessories", price: 149.99, quantity: 10, date: "2023-01-30" },
{ product: "Monitor", category: "Electronics", price: 299.99, quantity: 2, date: "2023-03-10" },
{ product: "Keyboard", category: "Accessories", price: 79.99, quantity: 7, date: "2023-02-05" }
];
const results = processSalesData(salesData);
console.log(results);
Python Implementation
# Python version
from datetime import datetime
from collections import defaultdict
def process_sales_data(sales_data):
# Calculate total revenue
total_revenue = sum(sale["price"] * sale["quantity"] for sale in sales_data)
# Group sales by category
category_sales = defaultdict(float)
for sale in sales_data:
category_sales[sale["category"]] += sale["price"] * sale["quantity"]
# Find top selling products
product_sales = defaultdict(int)
for sale in sales_data:
product_sales[sale["product"]] += sale["quantity"]
# Convert to list and sort by quantity
sorted_products = sorted(
[{"product": product, "quantity": quantity}
for product, quantity in product_sales.items()],
key=lambda x: x["quantity"],
reverse=True
)
top_products = sorted_products[:3]
# Calculate monthly sales
monthly_sales = defaultdict(float)
for sale in sales_data:
month = datetime.strptime(sale["date"], "%Y-%m-%d").month
monthly_sales[month] += sale["price"] * sale["quantity"]
return {
"total_revenue": total_revenue,
"category_sales": dict(category_sales),
"top_products": top_products,
"monthly_sales": dict(monthly_sales)
}
# Example usage
sales_data = [
{"product": "Laptop", "category": "Electronics", "price": 999.99, "quantity": 3, "date": "2023-01-15"},
{"product": "Smartphone", "category": "Electronics", "price": 699.99, "quantity": 5, "date": "2023-02-20"},
{"product": "Headphones", "category": "Accessories", "price": 149.99, "quantity": 10, "date": "2023-01-30"},
{"product": "Monitor", "category": "Electronics", "price": 299.99, "quantity": 2, "date": "2023-03-10"},
{"product": "Keyboard", "category": "Accessories", "price": 79.99, "quantity": 7, "date": "2023-02-05"}
]
results = process_sales_data(sales_data)
print(results)
Key Differences in This Example
- Python uses
defaultdictto simplify handling missing keys - Python uses a list comprehension for calculating total revenue
- Python's
sorted()function with akeyparameter versus JavaScript'ssort()method - Python uses slicing syntax (
[:3]) versus JavaScript'sslice()method - Python uses
datetime.strptime()for date parsing versus JavaScript'sDateconstructor
Conclusion
As a JavaScript developer learning Python, understanding the differences in loop constructs will help you write more idiomatic and efficient Python code. Here are the key takeaways:
- Python's
forloop is most similar to JavaScript'sfor...of, not the traditionalforloop - Use
range()in Python to create sequences of numbers for iteration - Python's
for...inon a dictionary iterates over keys, but it's more common to use.items()to get both keys and values - Python's list comprehensions can often replace
map()andfilter()operations with more concise syntax - Python has an unique
elseclause for loops that executes when the loop completes without abreak - Python's
enumerate()andzip()functions provide powerful ways to iterate over sequences
By understanding these differences and embracing Python's idiomatic patterns, you'll be able to write more effective and Pythonic code, leveraging your JavaScript knowledge while adopting Python's unique strengths.
Practice Exercises
Try these exercises to solidify your understanding of the differences between JavaScript and Python loops:
-
Convert JavaScript to Python: Convert this JavaScript for loop to Python:
// JavaScript const numbers = []; for (let i = 0; i < 10; i += 2) { numbers.push(i * i); } console.log(numbers); -
Convert JavaScript to Pythonic: Convert this JavaScript code to idiomatic Python using list comprehension:
// JavaScript const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const evenSquares = numbers .filter(n => n % 2 === 0) .map(n => n * n); -
Using Python's Loop Else: Write a Python function that checks if a list contains any prime numbers, using the loop
elseclause. -
Parallel Iteration: Write a function in both JavaScript and Python that takes two arrays/lists of equal length and returns an array/list of pairs.
-
Dictionary Processing: Write a function in both JavaScript and Python that filters a dictionary/object to include only key-value pairs where the value meets a certain condition.
-
Advanced Challenge: Implement a function in both languages that finds all possible pairs of numbers from an array/list that sum to a target value. Try to make your solutions as idiomatic as possible in both languages.