How to print odd numbers in Python
Learn how to print odd numbers in Python using different methods. Discover tips, real-world uses, and how to debug common errors.

Printing odd numbers is a common task in Python, perfect to practice loops and conditional logic. You can solve this with simple tools like the for loop and the modulo operator (%).
In this article, we'll cover several techniques to print odd numbers. We'll also share practical tips, explore real-world applications, and offer advice to help you debug your code effectively.
Using a for loop with range()
for num in range(1, 20, 2):
print(num, end=" ")--OUTPUT--1 3 5 7 9 11 13 15 17 19
This method is efficient because it lets the range() function do the heavy lifting. Instead of checking every number to see if it's odd, you generate only the odd numbers from the start. The key is the third argument in range(1, 20, 2), which sets the step.
- The loop starts at
1. - It stops before reaching
20. - It increments by
2each time, producing the sequence 1, 3, 5, and so on.
The print() function's end=" " argument simply replaces the default newline character with a space, which is why the output appears on a single line.
Basic techniques for printing odd numbers
While a for loop with a step is efficient, you can also tackle this with list comprehensions, the filter() function, or a classic while loop.
Using list comprehension with the modulo operator
odd_numbers = [num for num in range(1, 20) if num % 2 != 0]
print(odd_numbers)--OUTPUT--[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
List comprehensions offer a compact, readable way to create lists. This one-liner builds a new list named odd_numbers by iterating through a sequence and applying a condition. To understand the full capabilities of the range function, see our guide on using range in Python.
Here’s the breakdown:
- The code loops through each
numinrange(1, 20). - The condition
if num % 2 != 0checks if the number is odd. - If the condition is true, the
numis added to the new list.
Unlike the first method, this creates the entire list in memory before printing it.
Using filter() with a lambda function
odd_numbers = list(filter(lambda x: x % 2 != 0, range(1, 20)))
print(odd_numbers)--OUTPUT--[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
The filter() function provides a functional way to achieve the same result. It processes an iterable—in this case, range(1, 20)—and returns an iterator containing only the items that satisfy a specific condition. Here, a lambda function defines that condition on the fly.
- The expression
lambda x: x % 2 != 0is a concise, anonymous function that returnsTrueif a number is odd. filter()applies this function to each number, keeping only those that pass the test.- Since
filter()returns an iterator, you need to convert it to a list usinglist()to see all the odd numbers at once.
For a deeper understanding of lambda functions and their various applications, check out our comprehensive guide on using lambda functions in Python.
Using a while loop with increment
num = 1
while num < 20:
print(num, end=" ")
num += 2--OUTPUT--1 3 5 7 9 11 13 15 17 19
A while loop offers a more manual approach. You start by initializing a counter variable, in this case num = 1. The loop continues as long as the condition num < 20 remains true. It’s a classic loop structure that requires careful management of the counter.
- You must explicitly increment the counter inside the loop.
- The line
num += 2is critical. It ensures you only process odd numbers and, more importantly, prevents an infinite loop.
For more details on while loop patterns and best practices, see our guide on using while loops in Python.
Advanced methods and optimizations
Moving beyond the basics, you can optimize your code for memory and speed with specialized tools like itertools.islice(), generator functions, and the numpy library.
Using itertools.islice() for efficient iteration
import itertools
odds = itertools.islice(itertools.count(1), 0, 10, 2)
print(list(odds))--OUTPUT--[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
The itertools module offers powerful tools for memory-efficient iteration, especially with large or infinite sequences. This method combines two functions: itertools.count(), which generates numbers on demand, and itertools.islice(), which takes a slice from that sequence without loading it all into memory.
- The function
itertools.count(1)creates an iterator that yields numbers: 1, 2, 3, and so on, forever. islice()then extracts elements from that iterator. The arguments work just like list slicing, defining astart,stop, andstep.- This approach is highly efficient because it processes numbers one by one instead of building a full list first.
Creating a generator function for odd numbers
def odd_generator(limit):
num = 1
while num < limit:
yield num
num += 2
print(list(odd_generator(20)))--OUTPUT--[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
A generator function offers a custom, memory-efficient way to produce a sequence of values. Unlike a regular function that returns once, a generator can yield multiple values, pausing its execution between each one.
- The
yield numexpression sends back the current number and freezes the function's state. - When the next value is requested, the function resumes right where it left off.
This approach is powerful because it generates numbers on the fly, which is ideal for large datasets where you don't want to hold everything in memory at once.
Using numpy for vectorized operations
import numpy as np
odd_array = np.arange(1, 20, 2)
print(odd_array)--OUTPUT--[ 1 3 5 7 9 11 13 15 17 19]
For numerical tasks, the numpy library is a powerhouse. The np.arange() function works much like Python's built-in range(), but it returns a specialized numpy array. This is a go-to approach in data science and scientific computing because it’s built for speed.
numpyarrays are highly optimized for performance, especially with large datasets.- They enable vectorized operations—applying a function to an entire array at once instead of looping through each element.
While it might be overkill for this simple example, using np.arange() introduces a core concept for efficient data manipulation in Python, especially for AI coding.
Move faster with Replit
Replit is an AI-powered development platform where all Python dependencies pre-installed, so you can skip setup and start coding instantly. While learning individual techniques like using range() or list comprehensions is a great start, Agent 4 helps you move from piecing together code snippets to building complete, working applications.
Instead of just printing numbers, you can describe the app you want to build, and Agent 4 will take it from idea to a working product. For example:
- A log file analyzer that filters and displays entries from odd-numbered server instances to isolate specific traffic patterns.
- A batch ID generator that creates a sequence of unique, non-sequential identifiers for inventory management.
- A data sampling tool that extracts every Nth record from a large CSV file for quick statistical analysis without loading the entire dataset.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
Even a simple task like printing odd numbers can trip you up with off-by-one errors, tricky edge cases, and performance issues.
Off-by-one errors when using range() for odd numbers
A classic mistake is forgetting that the stop value in Python's range() function is exclusive. This means the loop runs up to, but does not include, the final number.
- If you write
range(1, 20), the last number generated is 19. If your intended limit was 20, this works fine. - However, if you wanted to include an odd number that is also your limit—say, 21—you'd need to use
range(1, 22). - Failing to add 1 to your upper bound is a common off-by-one error that can leave the last number out of your sequence.
Issues with negative numbers in is_odd() checks
The modulo operator (%) can behave in ways you might not expect with negative numbers. While Python's implementation is generally consistent, your logic needs to be robust.
- In Python,
-5 % 2evaluates to1, so a check likenum % 2 != 0correctly identifies it as odd. - The problem arises if you write the check as
num % 2 == 1. This works for positive numbers but can fail in other contexts or languages where the result of a negative modulo operation might be negative. - The most reliable way to check for an odd number, positive or negative, is to see if the remainder after dividing by 2 is not zero—
num % 2 != 0.
Performance pitfalls with repeated odd number generation
Constantly regenerating a list of odd numbers inside a loop or across multiple functions is inefficient. It's better to think about how you plan to use the sequence.
- If you need the same list of odd numbers multiple times, generate it once and store it in a variable. This avoids redundant computation.
- On the other hand, if you only need to iterate over the sequence once, a generator is far more memory-efficient than building a complete list, especially for very large ranges.
Off-by-one errors when using range() for odd numbers
It's easy to get tripped up by an off-by-one error with range() because its stop value is exclusive. This means the sequence it generates runs up to but won't include the end number, which can leave your results incomplete. See it in action below.
# Trying to get odd numbers up to 20
odd_numbers = list(range(1, 20, 2))
print(f"Last odd number: {odd_numbers[-1]}")
# Trying to check if 21 is in our sequence
if 21 in odd_numbers:
print("21 is included")
else:
print("21 is not included") # This will execute
The code generates a list ending at 19, so the check for 21 fails. This highlights how the exclusive nature of range() can cause you to miss the last number in your intended sequence. See the simple fix below.
# To include 21, adjust the range end
odd_numbers = list(range(1, 22, 2))
print(f"Last odd number: {odd_numbers[-1]}")
# Now 21 will be in our sequence
if 21 in odd_numbers:
print("21 is included") # This will execute
else:
print("21 is not included")
The fix is straightforward. To include a specific number like 21, you just need to increase the stop argument in range() to 22. Because range() is exclusive, it generates numbers up to but not including the stop value. It's a common pitfall to watch for whenever your loop's boundary is also a number you want to include in the final sequence. Always double-check your endpoints to avoid leaving out the last value.
Issues with negative numbers in is_odd() checks
The modulo operator (%) can behave unexpectedly with negative numbers, which often trips up simple odd-even checks. While an expression like num % 2 == 1 seems correct, it only works for positive values. See how this logic breaks down with a negative input.
def is_odd(num):
return num % 2 == 1 # This fails for negative numbers
print(is_odd(3)) # True (correct)
print(is_odd(-3)) # False (incorrect! -3 is odd)
The check num % 2 == 1 fails because the modulo operator’s result for negative odd numbers isn't always 1, causing the comparison to fail. A more robust check handles both positive and negative inputs correctly, as shown below.
def is_odd(num):
return num % 2 != 0 # Works for both positive and negative
print(is_odd(3)) # True
print(is_odd(-3)) # True
The fix is to check if the remainder is not zero using num % 2 != 0. This works because any number that isn't evenly divisible by 2 is odd, regardless of its sign. This simple change correctly handles Python's specific implementation of the modulo operator for negative numbers. It's a robust check you should use whenever your function might encounter negative inputs, like in data validation or numerical algorithms, to prevent unexpected behavior.
Performance pitfalls with repeated odd number generation
Constantly regenerating a list of odd numbers inside a loop is inefficient, especially when the list never changes. This redundant computation wastes resources and can slow your code down. The example below shows how this mistake appears in a process_data function.
def process_data(data_chunks):
results = []
for chunk in data_chunks:
# Inefficient: regenerating odd numbers for each chunk
odds = [n for n in range(1, 100) if n % 2 != 0]
results.append(sum(x * y for x, y in zip(chunk, odds[:len(chunk)])))
return results
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(process_data(data))
The odds list is recreated on each pass of the loop, even though its contents never change. This adds needless overhead. The corrected code below shows a more efficient approach.
def process_data(data_chunks):
# Generate odd numbers just once
odds = [n for n in range(1, 100) if n % 2 != 0]
results = []
for chunk in data_chunks:
results.append(sum(x * y for x, y in zip(chunk, odds[:len(chunk)])))
return results
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(process_data(data))
The fix is simple but effective: generate the odds list once, outside the loop. This prevents the program from doing the same work on every single iteration, saving valuable processing power. You should watch for this inefficiency whenever a loop relies on static data that doesn't change. Pre-calculating it beforehand is a key optimization, especially when working with large datasets or functions that are called frequently.
Real-world applications
The logic for handling odd numbers goes beyond simple loops, powering applications from creating checkerboard patterns to implementing sorting algorithms.
Creating a checkerboard pattern with odd coordinates
The logic for identifying odd numbers is perfect for visual tasks, like creating a checkerboard pattern by checking if the sum of a cell's coordinates, i + j, is odd.
def create_checkerboard(size=5):
board = []
for i in range(size):
row = []
for j in range(size):
cell = 'X' if (i + j) % 2 != 0 else ' '
row.append(cell)
board.append(''.join(row))
return board
for row in create_checkerboard(5):
print(row)
The create_checkerboard function builds the grid using nested for loops to manage the rows and columns. A conditional expression then places either an 'X' or a space in each cell to form the alternating pattern.
- An inner list,
row, gathers the characters for each horizontal line of the board. - The
''.join(row)method efficiently converts this list of characters into a single string.
Finally, the function returns a list containing all the completed row strings, which are then printed to display the checkerboard.
Implementing the odd-even sort algorithm using range()
The odd-even sort is a simple algorithm that uses range() with a step of two to sweep through a list, repeatedly comparing and swapping adjacent elements at odd and even indices until the list is ordered.
def odd_even_sort(arr):
n = len(arr)
sorted = False
while not sorted:
sorted = True
# Process odd indices
for i in range(1, n-1, 2):
if arr[i] > arr[i+1]:
arr[i], arr[i+1] = arr[i+1], arr[i]
sorted = False
# Process even indices
for i in range(0, n-1, 2):
if arr[i] > arr[i+1]:
arr[i], arr[i+1] = arr[i+1], arr[i]
sorted = False
return arr
print(odd_even_sort([5, 3, 8, 4, 2, 9, 1, 7, 6]))
The odd_even_sort function organizes a list by repeatedly sweeping through it. A while loop continues as long as swaps are being made, a state tracked by the sorted flag. Each pass through the loop involves two phases.
- First, it compares and swaps adjacent elements at odd-indexed pairs like
arr[1]andarr[2]. - Next, it does the same for even-indexed pairs, such as
arr[0]andarr[1].
If any pair is out of order, the elements are swapped, and the sorted flag is reset to False, ensuring the loop runs again.
Get started with Replit
Now, turn these concepts into a real tool. Tell Replit Agent to “build a script that samples odd-numbered rows from a CSV” or “create a utility that generates unique odd-numbered batch IDs.”
It will write the code, test for errors, and deploy your application for you. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



