Using both Python 2.x and Python 3.x in IPython Notebook
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If you still need both Python 2 and Python 3 in notebooks, the correct model is separate kernels, not one notebook process somehow switching interpreters mid-session. Modern Jupyter makes that possible, but Python 2 is end-of-life, so this setup should be treated as a legacy compatibility bridge rather than a long-term development strategy.
The Core Idea: One Kernel Per Interpreter
A notebook server can expose multiple kernels. Each kernel points at a specific interpreter and environment. That means:
- one kernel can run Python 3
- another kernel can run Python 2
- each notebook chooses one kernel at a time
You are not mixing both runtimes inside one executed notebook cell stream. You are selecting between distinct environments.
Create Separate Environments
The safest setup is one isolated environment for Python 2 and one for Python 3. If you still have a Python 2 interpreter available, install a dedicated kernel package there.
Python 3 environment:
Legacy Python 2 environment:
After that, the Jupyter interface can list both kernels.
Launch Jupyter and Select the Kernel
Start the notebook server from any environment that has Jupyter installed:
When creating or opening a notebook, choose the kernel from the UI. Existing notebooks can also be reassigned to a different kernel, but remember that the code inside the notebook may not be compatible across versions.
Keep Dependencies Separate
The kernel name is only part of the story. The environment behind it also controls package versions. If one notebook needs pandas for Python 3 and another requires a legacy Python 2 library, install each dependency inside the matching environment instead of globally.
This is the main benefit of kernels over ad hoc interpreter switching. Each notebook runtime becomes reproducible.
Verify Which Python Is Running
Never assume you selected the correct kernel. Print the interpreter path directly in the notebook:
This is especially useful on shared systems where multiple kernels have similar names.
Compatibility Realities in 2026
Python 2 has been end-of-life for years. That means:
- many libraries no longer publish Python 2 compatible versions
- security support is gone
- packaging tools may break in newer environments
If you only need to inspect or migrate old notebooks, keep the Python 2 kernel as isolated and minimal as possible. Do not build new application logic around it.
A Better Medium-Term Strategy
If the legacy notebook still matters, a practical migration path is:
- keep the original notebook runnable with the legacy kernel
- create a Python 3 copy
- port imports, print statements, and text-handling differences incrementally
- remove the Python 2 kernel once the migrated notebook passes validation
That lets you preserve a working reference while moving toward a maintainable environment.
Common Pitfalls
- Expecting one notebook kernel to switch between Python 2 and Python 3 during execution.
- Installing Jupyter globally and losing track of which environment owns which kernel.
- Reusing the same display name for multiple kernels and selecting the wrong one.
- Assuming package availability will be similar across Python 2 and Python 3.
- Keeping Python 2 around longer than necessary after the migration work is done.
Summary
- Use separate Jupyter kernels, one per interpreter version.
- Isolate Python 2 and Python 3 in different environments.
- Verify the active interpreter inside the notebook with
sys.versionandsys.executable. - Treat Python 2 support as a temporary compatibility layer, not a new baseline.
- Plan a migration path so the legacy kernel can eventually be removed.

