Every Python developer has lived the ritual: install the right interpreter, create an isolated environment so project A’s Django 4 doesn’t fight project B’s Django 5, then install dependencies without poisoning the system Python. For fifteen years that ritual meant juggling three separate tools — virtualenv for environments, pyenv for interpreter versions, pip for packages. Then uv arrived and collapsed all three into one Rust binary that its own README describes as a replacement for “pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more.” In 2026 the question is no longer “which virtual environment tool do I use?” but “do I still need a separate environment tool at all?” This guide compares uv (89,629 stars, MIT), pyenv (45,082 stars, MIT), and virtualenv (5,045 stars, MIT) so you can pick a strategy instead of a tool.
TL;DR / Quick Verdict
For any new project in 2026, standardize on uv — it manages Python versions, creates environments, resolves and locks dependencies, and runs tools, all with a global cache and a documented 10-100x speedup over pip for resolution and installation. Keep pyenv only if you already run a multi-interpreter workflow (testing a library across Python 3.9-3.14) and do not want to change muscle memory — though uv’s uv python install covers the same job. Keep virtualenv only for legacy constraints: supporting ancient interpreters that uv and pyenv no longer target, or environments where you are forbidden from installing new tooling. The pragmatic 2026 stack is uv for everything new, pyenv or virtualenv left in place where they already work, because both remain perfectly maintained — they are just no longer the default answer.
Head-to-Head Comparison Table
| Dimension | uv | pyenv | virtualenv |
|---|---|---|---|
| Stars (Sep 2026) | 89,629 | 45,082 | 5,045 |
| License | MIT | MIT | MIT |
| Written in | Rust | Shell (bash) | Python |
| Last push (Sep 2026) | 2026-09-08 | 2026-09-06 | 2026-09-08 |
| Primary job | Packages + envs + Python versions | Python version switching | Environment creation |
| Manages interpreters | Yes (uv python install) | Yes (pyenv install) | No |
| Creates environments | Yes (uv venv) | Via pyenv-virtualenv plugin | Yes |
| Installs packages | Yes (uv pip / uv add) | No | No (seed pip only) |
| Lockfile support | Universal lockfile (uv lock) | No | No |
| Speed | 10-100x faster than pip (vendor claim) | n/a (shell shims) | Fast (app-data seeding) |
| Install | curl installer or pip | git clone or package manager | pip install |
| Works with old Pythons | 3.8+ managed targets | Broad (compiles) | Down to Python 2.x era |
| Best for | New projects, CI, monorepos | Multi-version testing | Legacy environments |
Decision Matrix: Pick in 10 Seconds
| Use case | Recommendation | Why |
|---|---|---|
| New application or library, 2026 | uv | One tool for interpreter + env + deps + lockfile; fastest installs; project files are standard (pyproject.toml, .python-version) |
| Testing a library across Python 3.9 → 3.14 | uv or pyenv | uv python install 3.12 3.13 3.14 matches pyenv install for CI matrix setup |
| Legacy system with Python 2-era or EOL interpreters | virtualenv | Works where modern tooling refuses to; environments are plain directories |
| Air-gapped or policy-restricted servers | virtualenv + pip | No new binaries, no downloads of managed interpreters |
| Existing pyenv shop, happy with it | Stay on pyenv | It is maintained and stable; migrate only when the workflow hurts |
| CI where install time dominates | uv | Sub-second resolution and installs with a warm cache |
uv — The All-in-One That Actually Delivered
uv’s README leads with a claim that would have sounded absurd in 2023: it is “an extremely fast Python package and project manager, written in Rust,” and its highlights list says it is “a single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more,” backed by benchmark claims of being 10-100x faster than pip. It is developed by Astral, the team behind Ruff, and it installs with a standalone curl script — no Rust toolchain, no Python required:
| |
The project workflow is a single command chain — initialize, add a dependency, run, lock, sync — and the README’s own console session shows how fast the loop is:
| |
Notice what just happened: uv add created the environment, resolved dependencies, and installed them — replacing virtualenv, pip, and a lockfile tool in one command. For teams that do not want the full project model, uv keeps a pip-compatible interface that is a drop-in replacement: uv venv creates a .venv, uv pip compile requirements.in --universal produces a platform-independent requirements file, and uv pip sync requirements.txt installs it (the README example resolves 43 packages in 12ms and installs them in 208ms).
uv also absorbs pyenv’s job. It installs and switches Python versions on demand:
| |
The uv python pin command writes a .python-version file — the same convention pyenv popularized — which means uv and pyenv can interoperate on interpreter selection if they coexist on one machine.
pyenv — The Version Manager That Refuses to Die
pyenv is “simple Python version management,” and its genius is the mechanism: shims. pyenv inserts a directory of shim executables at the front of your PATH; when you run python, the shim consults version-selection rules and dispatches to the right interpreter. Selection is scoped the way developers actually think: pyenv shell <version> selects for the current shell session, pyenv local <version> selects automatically whenever you are in the current directory or its subdirectories, and pyenv global <version> sets the default for your user account. The per-directory .python-version file that pyenv local writes is the same convention uv’s python pin honors — one of the reasons pyenv’s design aged so well.
Installing interpreters is straightforward and version-complete:
| |
The honest caveats: pyenv compiles Python from source on Unix, so it depends on the build toolchain and development libraries (the README carries a dedicated “Install Python build dependencies” section — on macOS that means brew install of openssl, readline, sqlite, and friends). Compiling 3.x takes minutes per version, and because pyenv manages interpreters only, you still need virtualenv or python -m venv for isolation and pip for packages. Its longevity is a feature: it has worked the same way for a decade, and its plugin ecosystem (including pyenv-virtualenv) covers the gaps — but in 2026 it is a specialist tool rather than a complete workflow.
virtualenv — The Battle-Tested Base Layer
virtualenv, maintained by the Python Packaging Authority (PyPA), describes itself simply as “a tool for creating isolated virtual python environments.” It predates the standard library’s venv module and remains useful precisely where python -m venv falls short: it works across a much wider range of interpreter versions (including interpreters too old for the stdlib module), it is pip-installable and upgradable independently of any Python, and it seeds new environments with pip so you can install packages immediately. In practice you invoke it directly or through a wrapper:
| |
The modern guidance is to prefer python -m venv when your interpreter is new enough, and to remember that neither venv nor virtualenv solves the version selection problem or the dependency resolution problem — they only isolate. That is why virtualenv’s role has shrunk to a base layer inside bigger workflows: pyenv users pair it with pyenv-virtualenv, uv users never touch it because uv venv replaces it, and legacy deployments keep using it because it is boring, stable, and universal.
Migration and Operational Pitfalls
- Do not install three tools to do one job. If you adopt uv, the common failure mode is keeping pyenv + virtualenv + pip habits and layering uv on top — now you have two resolvers, two environment layouts, and two sources of truth. Pick uv as the default, and use
uv venv-created.venvdirectories everywhere, including systemd units and cron jobs (call.venv/bin/pythonby absolute path — never rely on activation in non-interactive contexts). - pyenv compiles from source — budget for it. A missing OpenSSL/readline/sqlite dev package fails
pyenv installwith cryptic build errors, and each version takes minutes to compile. On CI, prefer prebuilt managed Pythons (uv downloads prebuilt builds; pyenv can usepyenv installwith mirror settings but still compiles by default) or cache the compiled versions. - Shim ordering and nested shells. pyenv’s shims must sit at the front of
PATHor they silently pass through to the system Python — the README’s “Understanding PATH” and “Understanding Shims” sections exist because this bites everyone once. Nested shells spawned from Python-based programs need the documented environment variables, not just aPATHexport. .python-versionis shared convention. uv’suv python pinand pyenv’spyenv localboth write.python-version, which is convenient — but it also means whichever tool runs last wins. If both tools are installed, commit the file and standardize who writes it, or your CI can resolve a different interpreter than your laptop.- Vendor speed claims need a control. uv’s “10-100x faster than pip” is documented against its own benchmarks and is very real for resolution with a warm cache — but cold downloads on a slow network are network-bound like anything else. Measure your own
uv pip syncagainstpip install -rin your CI before quoting numbers to stakeholders. - Lock the tool, not just the deps. uv moves fast; pin the uv version in CI (the standalone installer supports
uv self update, and Docker images should pin the release) or you will debug a resolver change that arrived between Thursday and Friday. - Air-gapped environments. uv’s managed-Python downloads and its global cache assume network access (or a pre-seeded cache). In fully isolated networks,
virtualenv+pip --no-indexagainst a local index remains the least surprising path.
Python environment tooling is only one layer of the packaging story. Our Python dependency management comparison (Poetry vs Pipenv vs Hatch vs PDM) covers the lockfile-and-resolution layer uv also competes with, and the Python CLI testing guide (Click vs Typer vs argparse) shows how uv’s tool-running mode (uvx, uv tool install) changes the way you ship command-line utilities. If your concurrency model depends on specific interpreters — the gevent and eventlet model covered in our Python async concurrency comparison — remember that interpreter selection is exactly what pyenv and uv manage for you.
FAQ
Does uv replace virtualenv and pyenv?
Yes, for modern Python. uv creates environments (uv venv), installs packages (uv add, uv pip sync), manages interpreters (uv python install), and pins versions (uv python pin, writing .python-version). Its README explicitly lists pyenv and virtualenv among the tools it replaces. Virtualenv remains relevant for very old interpreters; pyenv remains relevant for teams that prefer its shell-based workflow.
Is uv faster than pip? In resolution and installation, typically by a large margin — the project documents a 10-100x speedup over pip in its benchmarks, and the effect is most visible with a warm cache (the README shows 43 packages resolved in 12ms and installed in 208ms). Cold downloads over slow networks are still network-bound.
Can uv and pyenv coexist?
Yes. Both honor the .python-version file convention — pyenv local and uv python pin write it — so they can interoperate on interpreter selection. The pitfall is having both tools write that file in the same repo; standardize on one writer.
Do I still need to activate a virtual environment?
No — and in scripts, systemd units, and cron jobs you should not. Call the environment’s binaries by absolute path (.venv/bin/python) instead of relying on activate, which only mutates your shell session and is easy to miss in non-interactive contexts.
Is pyenv obsolete in 2026? Not obsolete — it is stable, maintained (last push September 2026), and still the best-known tool for testing across many interpreter versions. But its scope is deliberately narrow: interpreters only. For a complete modern workflow, uv covers the same version-management job plus environments and dependencies.
What should I use for a legacy Python 2 application? virtualenv is the safest choice for environments around interpreters that modern tooling no longer manages. It remains maintained by PyPA and supports creating isolated environments for older interpreters, while uv and pyenv target current and recent Python versions.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com