Migrating from Python 2 to Python 3

Published: (December 9, 2025 at 11:44 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Python 2 reached its end‑of‑life in January 2020, but many legacy projects still run on it. Migrating to Python 3 is essential to leverage modern features, better performance, security updates, and long‑term support. However, the migration process is not always trivial. Below are the main challenges, practical strategies, and code examples for a smooth transition.

1. Understanding the Key Differences

  • Print is now a function

    # Python 2
    print "Hello"
    # Python 3
    print("Hello")
  • Integer division behavior

    # Python 2
    5 / 2   # → 2 (floor division)
    # Python 3
    5 / 2   # → 2.5
    # Use // for integer division
    5 // 2  # → 2
  • Unicode strings by default
    Python 3 strings are Unicode (str), whereas Python 2 distinguishes str (bytes) and unicode.

  • Iterators instead of lists for built‑ins
    Functions like range(), map(), filter(), and zip() now return iterators instead of lists.

  • Exception syntax

    # Python 2
    except Exception, e:
        ...
    
    # Python 3
    except Exception as e:
        ...

2. Preparing for Migration

Audit the Codebase

Identify Python 2‑specific syntax, libraries, and dependencies.

Check Dependencies

Ensure all third‑party packages support Python 3. Use pip list and verify compatibility.

Set Up a Virtual Environment

Create a Python 3 virtual environment to test migration without affecting your existing setup:

python3 -m venv py3env
source py3env/bin/activate

3. Using Automated Tools

2to3 Tool

Scans code and suggests changes to Python 3 syntax.

2to3 -w myproject/

-w writes changes in place. Review the modifications carefully, especially for complex code.

futurize / modernize

Libraries that help make code compatible with both Python 2 and 3 during incremental migration.

pip install future
futurize -w myproject/

4. Common Manual Fixes

String handling

# Python 2
s = u"Hello"
print type(s)  # 

# Python 3
s = "Hello"
print(type(s))  # 

Dictionary iteration

# Python 2
for key in mydict.iterkeys():
    print(key)

# Python 3
for key in mydict.keys():
    print(key)

Handling xrange

Replace xrange() with range() in Python 3.

5. Testing and Validation

Unit Tests

Run existing tests under Python 3 and fix failing tests one by one.

CI/CD Pipeline

Test Python 3 code in a separate branch or pipeline before merging.

Incremental Migration

If the codebase is large, migrate module by module.

6. Handling Third‑Party Libraries

Some libraries may not be Python 3 compatible. Options include:

  • Upgrade to the latest version if available.
  • Replace with a modern alternative.
  • Use compatibility layers like six for supporting both Python 2 and 3 during transition.

7. Best Practices Post‑Migration

  • Use Python 3.10+ for access to match‑case statements, type hinting, and performance improvements.

  • Remove Python 2 compatibility code to reduce complexity.

  • Embrace f‑strings for clean string formatting:

    name = "Alice"
    age = 30
    print(f"{name} is {age} years old")

Conclusion

Migrating from Python 2 to Python 3 can seem daunting, but with proper planning, automated tools, and careful testing, it’s achievable. Focus on code quality, dependency compatibility, and comprehensive testing to ensure a smooth transition.

Key Takeaways

  • Audit and prepare the codebase.
  • Use 2to3 or futurize to automate syntax changes.
  • Test extensively and migrate incrementally.
  • Embrace Python 3 features for cleaner, safer, and more efficient code.
Back to Blog

Related posts

Read more »