Python中的版本号比较

100 投票
17 回答
72167 浏览
提问于 2025-04-15 15:53

我想写一个类似于 cmp 的函数,用来比较两个版本号,并根据比较的结果返回 -101

  • 如果版本 A 比版本 B 旧,就返回 -1
  • 如果版本 A 和版本 B 是一样的,就返回 0
  • 如果版本 A 比版本 B 新,就返回 1

每个小部分都应该被看作一个数字,所以 1.10 是大于 1.1 的。

期望的函数输出是

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
...

这是我实现的代码,欢迎改进:

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

顺便说一下,我使用的是 Python 2.4.5(在我工作地方安装的...)。

这里有一个小的“测试套件”,你可以用来测试

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

17 个回答

31

在这种情况下,重用算不算优雅呢? :)

# pkg_resources is in setuptools
# See http://peak.telecommunity.com/DevCenter/PkgResources#parsing-utilities
def mycmp(a, b):
    from pkg_resources import parse_version as V
    return cmp(V(a),V(b))
293

你可以试试用Python的 distutils.version.StrictVersion 来处理版本号。

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

对于你的 cmp 函数来说:

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

如果你想比较更复杂的版本号,distutils.version.LooseVersion 会更有用,不过要确保你比较的是同类型的版本号。

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

不过,LooseVersion 并不是最聪明的工具,容易被欺骗:

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

要想用好这个工具,你需要跳出标准库,使用 setuptools 提供的解析工具 parse_version

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

所以,根据你的具体需求,你需要决定是使用内置的 distutils 工具就够了,还是需要添加 setuptools 作为依赖。

41

去掉字符串中不重要的部分(比如末尾的零和点),然后再比较数字列表。

import re

def mycmp(version1, version2):
    def normalize(v):
        return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
    return cmp(normalize(version1), normalize(version2))

这和Pär Wieslander的方法差不多,但稍微简洁一些:

这里有一些测试,感谢“如何在Bash中比较两个以点分隔的版本字符串?”:

assert mycmp("1", "1") == 0
assert mycmp("2.1", "2.2") < 0
assert mycmp("3.0.4.10", "3.0.4.2") > 0
assert mycmp("4.08", "4.08.01") < 0
assert mycmp("3.2.1.9.8144", "3.2") > 0
assert mycmp("3.2", "3.2.1.9.8144") < 0
assert mycmp("1.2", "2.1") < 0
assert mycmp("2.1", "1.2") > 0
assert mycmp("5.6.7", "5.6.7") == 0
assert mycmp("1.01.1", "1.1.1") == 0
assert mycmp("1.1.1", "1.01.1") == 0
assert mycmp("1", "1.0") == 0
assert mycmp("1.0", "1") == 0
assert mycmp("1.0", "1.0.1") < 0
assert mycmp("1.0.1", "1.0") > 0
assert mycmp("1.0.2.0", "1.0.2") == 0

撰写回答