Python 3
NameError
xrange
programming error
Python migration

NameError global name 'xrange' is not defined in Python 3

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The error NameError: global name 'xrange' is not defined appears in Python 3 because xrange() was removed. The fix is usually simple: replace xrange(...) with range(...), because Python 3 range behaves like Python 2 xrange in the important ways that matter for iteration.

Why xrange Existed in Python 2

Python 2 had two similar built-ins:

  • 'range() created a real list in memory'
  • 'xrange() produced numbers lazily as you iterated'

That meant xrange() was the better choice for large loops in Python 2:

python
# Python 2 style
for i in xrange(1000000):
    pass

Python 3 simplified this design by removing xrange() and making range() lazy and memory-efficient.

The Python 3 Fix

In Python 3, write:

python
for i in range(1000000):
    pass

That is the direct replacement in almost all migrated code.

You can also inspect the object:

python
r = range(5)
print(r)           # range(0, 5)
print(list(r))     # [0, 1, 2, 3, 4]

This shows that range() is not pre-building the full list by default.

range in Python 3 Is Not the Old Python 2 range

This is the key migration insight. In Python 3:

  • 'range() is iterable'
  • it is memory-efficient
  • it supports indexing and slicing behavior similar to a sequence

So if your old Python 2 code used xrange() for performance, range() is now the intended replacement.

When You Actually Need a List

If the code truly needs a materialized list, convert explicitly:

python
numbers = list(range(5))
print(numbers)  # [0, 1, 2, 3, 4]

This matters if:

  • you need list methods such as .append
  • a library expects a concrete list
  • you want to serialize or print the full sequence as list data

Do not call list(range(...)) automatically unless the code really needs it, because that throws away the memory advantage.

Updating Old Compatibility Code

Sometimes older code tried to support both Python 2 and Python 3. A common migration cleanup is simply to remove xrange entirely and standardize on range.

For example, change:

python
for i in xrange(len(items)):
    print(items[i])

to:

python
for i in range(len(items)):
    print(items[i])

Or, even better, rewrite the loop more idiomatically:

python
for item in items:
    print(item)

Migration is a good chance to simplify code, not only to rename built-ins mechanically.

A Better Python 3 Style

Many loops that once used xrange do not need index-based iteration at all:

python
1items = ["a", "b", "c"]
2
3for index, item in enumerate(items):
4    print(index, item)

This is usually clearer than for i in range(len(items)) unless you specifically need index arithmetic.

Common Pitfalls

The biggest mistake is trying to define xrange = range everywhere as a long-term fix. That may patch the immediate error, but it keeps old Python 2 habits alive instead of moving the codebase cleanly to Python 3 style.

Another issue is assuming Python 3 range() returns a list and then trying to use list-only operations on it. If you need a list, convert it explicitly with list(range(...)).

Finally, do not rewrite every old xrange loop mechanically without reviewing the loop logic. Many of those loops can be improved with direct iteration or enumerate, which makes the migrated code more readable.

Summary

  • 'xrange() does not exist in Python 3.'
  • Replace it with range() in almost all migrated code.
  • Python 3 range() is lazy and memory-efficient like Python 2 xrange().
  • Use list(range(...)) only when you truly need a real list.
  • Treat the migration as a chance to modernize loop style, not just rename functions.

Course illustration
Course illustration

All Rights Reserved.