How do I compare version numbers in Python?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Version strings look simple until plain string comparison gives the wrong answer. For example, 2.10 is newer than 2.9, but lexical comparison treats those values as text rather than as version components.
In Python, the reliable approach is to parse versions into version-aware objects and compare those objects. If you are working with Python package versions, the packaging library is the practical default because it understands release candidates, post releases, and other real-world version forms.
Use packaging.version.Version
For Python-style package versions, packaging.version.Version is the right tool:
This is better than custom parsing because it already knows how to order pre-releases, final releases, and post releases correctly.
It also makes the intent obvious. Anyone reading the code can see that these values are versions, not arbitrary strings.
Why Raw String Comparison Fails
Plain string comparison is character-based:
That result is not trustworthy for versioning. It compares the characters from left to right rather than comparing numeric segments and release semantics.
Even a hand-rolled tuple parser becomes fragile once suffixes appear. A format that starts as major.minor.patch often grows into 1.0rc1, 1.0.post1, or 2.0.dev0. At that point, the simple parser has to become a version parser, and you are reimplementing logic that already exists.
Sort and Filter Versions Correctly
Once versions are parsed, sorting is straightforward:
Range checks are also common. For those, SpecifierSet is useful:
This is especially helpful in deployment tooling, compatibility validation, and feature gating.
Wrap the Logic in a Small Helper
If your code compares versions in several places, a helper keeps error handling in one place:
This is clearer than scattering Version(...) calls and exception handling through unrelated business code.
When a Simple Tuple Is Good Enough
Sometimes you control the format completely and know the values are always numeric segments with no suffixes. In that narrow case, a tuple comparison is acceptable:
This is fine for tightly controlled internal data. It is not a good general solution for package versions, external APIs, or any version scheme that may evolve.
Common Pitfalls
The most common mistake is comparing raw strings directly. That works only when the values happen to line up with lexical ordering.
Another common issue is writing a custom parser too early. It may look shorter on day one, but it becomes technical debt as soon as version suffixes or ecosystem-specific rules appear.
Developers also assume every version scheme follows the same rules. Python package versions, Docker tags, mobile app versions, and vendor-specific build numbers may all have different semantics.
Finally, do not silently ignore invalid version strings. If an input is malformed, fail clearly and early instead of treating it as a zero or leaving it to string comparison by accident.
Summary
- Parse version strings before comparing them.
- Use
packaging.version.Versionfor Python package-style versions. - Use
SpecifierSetfor range checks such as compatibility constraints. - A tuple parser is acceptable only for very simple, fixed numeric formats.
- Avoid raw string comparison unless you want incorrect results.

