Version number comparison in Python

Johannes Charra picture Johannes Charra · Nov 11, 2009 · Viewed 56.4k times · Source

I want to write a cmp-like function which compares two version numbers and returns -1, 0, or 1 based on their compared valuses.

  • Return -1 if version A is older than version B
  • Return 0 if version A and B are equivalent
  • Return 1 if version A is newer than version B

Each subsection is supposed to be interpreted as a number, therefore 1.10 > 1.1.

Desired function outputs are

mycmp('1.0', '1') == 0
mycmp('1.0.0', '1') == 0
mycmp('1', '1.0.0.1') == -1
mycmp('12.10', '11.0.0.0.0') == 1
...

And here is my implementation, open for improvement:

def mycmp(version1, version2):
    parts1 = [int(x) for x in version1.split('.')]
    parts2 = [int(x) for x in version2.split('.')]

    # fill up the shorter version with zeros ...
    lendiff = len(parts1) - len(parts2)
    if lendiff > 0:
        parts2.extend([0] * lendiff)
    elif lendiff < 0:
        parts1.extend([0] * (-lendiff))

    for i, p in enumerate(parts1):
        ret = cmp(p, parts2[i])
        if ret: return ret
    return 0

I'm using Python 2.4.5 btw. (installed at my working place ...).

Here's a small 'test suite' you can use

assert mycmp('1', '2') == -1
assert mycmp('2', '1') == 1
assert mycmp('1', '1') == 0
assert mycmp('1.0', '1') == 0
assert mycmp('1', '1.000') == 0
assert mycmp('12.01', '12.1') == 0
assert mycmp('13.0.1', '13.00.02') == -1
assert mycmp('1.1.1.1', '1.1.1.1') == 0
assert mycmp('1.1.1.2', '1.1.1.1') == 1
assert mycmp('1.1.3', '1.1.3.000') == 0
assert mycmp('3.1.1.0', '3.1.2.10') == -1
assert mycmp('1.1', '1.10') == -1

Answer

bradley.ayers picture bradley.ayers · Aug 7, 2011

How about using Python's distutils.version.StrictVersion?

>>> from distutils.version import StrictVersion
>>> StrictVersion('10.4.10') > StrictVersion('10.4.9')
True

So for your cmp function:

>>> cmp = lambda x, y: StrictVersion(x).__cmp__(y)
>>> cmp("10.4.10", "10.4.11")
-1

If you want to compare version numbers that are more complex distutils.version.LooseVersion will be more useful, however be sure to only compare the same types.

>>> from distutils.version import LooseVersion, StrictVersion
>>> LooseVersion('1.4c3') > LooseVersion('1.3')
True
>>> LooseVersion('1.4c3') > StrictVersion('1.3')  # different types
False

LooseVersion isn't the most intelligent tool, and can easily be tricked:

>>> LooseVersion('1.4') > LooseVersion('1.4-rc1')
False

To have success with this breed, you'll need to step outside the standard library and use setuptools's parsing utility parse_version.

>>> from pkg_resources import parse_version
>>> parse_version('1.4') > parse_version('1.4-rc2')
True

So depending on your specific use-case, you'll need to decide whether the builtin distutils tools are enough, or if it's warranted to add as a dependency setuptools.