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.

How to print odd numbers in Python
Published on: 
Tue
Feb 24, 2026
Updated on: 
Mon
Apr 6, 2026
The Replit Team

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 2 each 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 num in range(1, 20).
  • The condition if num % 2 != 0 checks if the number is odd.
  • If the condition is true, the num is 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 != 0 is a concise, anonymous function that returns True if 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 using list() 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 += 2 is 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 a start, stop, and step.
  • 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 num expression 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.

  • numpy arrays 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 % 2 evaluates to 1, so a check like num % 2 != 0 correctly 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] and arr[2].
  • Next, it does the same for even-indexed pairs, such as arr[0] and arr[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.

Build your first app today

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.

Build your first app today

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.