Python中的版本号比较

Joh*_*rra 95 python string-comparison

我想写一个cmp样功能,比较两个版本号,并返回-1,01根据自己的比较valuses.

  • -1如果版本A早于版本B,则返回
  • 返回0如果版本A和B是等价的
  • 1如果版本A比版本B更新,则返回

每个子部分应该被解释为一个数字,因此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
...
Run Code Online (Sandbox Code Playgroud)

这是我的实施,开放改进:

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
Run Code Online (Sandbox Code Playgroud)

我正在使用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
Run Code Online (Sandbox Code Playgroud)

bra*_*ers 271

用Python怎么样distutils.version.StrictVersion

>>> from distutils.version import StrictVersion
>>> StrictVersion('10.4.10') > StrictVersion('10.4.9')
True
Run Code Online (Sandbox Code Playgroud)

所以对你的cmp功能:

>>> cmp = lambda x, y: StrictVersion(x).__cmp__(y)
>>> cmp("10.4.10", "10.4.11")
-1
Run Code Online (Sandbox Code Playgroud)

如果要比较更复杂的版本号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
Run Code Online (Sandbox Code Playgroud)

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

>>> LooseVersion('1.4') > LooseVersion('1.4-rc1')
False
Run Code Online (Sandbox Code Playgroud)

要想成功使用这个品种,您需要走出标准库并使用setuptools的解析实用程序parse_version.

>>> from pkg_resources import parse_version
>>> parse_version('1.4') > parse_version('1.4-rc2')
True
Run Code Online (Sandbox Code Playgroud)

因此,根据您的具体用例,您需要确定内置distutils工具是否足够,或者是否有必要将其添加为依赖项setuptools.

  • 但请注意,"StrictVersion"******最多可以使用三个版本.它失败了像'0.4.3.6`这样的东西! (13认同)
  • 在这个答案中,每个`distribute`实例都应该被`setuptools`取代,它与`pkg_resources`包捆绑在一起,从那时起......就像_ever_.同样,这是与`setuptools捆绑在一起的`pkg_resources.parse_version()`函数的[官方文档](https://setuptools.readthedocs.io/en/latest/pkg_resources.html#id33). (6认同)
  • 任何时候你找不到文档,尝试导入包并使用help(). (3认同)
  • 似乎最有意义的是只使用已经存在的东西:) (2认同)
  • 太好了!你有没有通过阅读来源解决这个问题?我无法在任何地方找到distutils.version的文档: - / (2认同)
  • 我认为没有任何文档.是的,当我考虑编写自己的包装解决方案时,我正在阅读源代码,但后来我找到了distutils2. (2认同)

gnu*_*nud 36

删除字符串中不感兴趣的部分(尾随零和点),然后比较数字列表.

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))
Run Code Online (Sandbox Code Playgroud)

编辑:与PärWieslander相同的方法,但更紧凑.

一些测试,感谢这篇文章:

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
Run Code Online (Sandbox Code Playgroud)

  • 我担心它不会起作用,`rstrip(".0")`会在"1.0.10"中将".10"改为".1". (2认同)
  • 注意 cmp() 已在 Python 3 中删除:https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons (2认同)

con*_*nny 30

重用认为优雅在这种情况下?:)

# 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))
Run Code Online (Sandbox Code Playgroud)

  • 嗯,当你提到[标准库之外](http://www.python.org/dev/peps/pep-0365/)而没有解释它到哪里时,它并不是那么优雅.我提交了一个包含URL的编辑.就个人而言,我更喜欢使用distutils - 为了这么简单的任务而花费第三方软件似乎并不值得. (6认同)
  • @adam-spiers _wut?_ 你有没有读过评论?`pkg_resources` 是一个 `setuptools` 捆绑包。由于 `setuptools` 在所有 Python 安装中实际上是强制性的,`pkg_resources` 在任何地方都有效。也就是说,`distutils.version` 子包也很有用——尽管比更高级别的 `pkg_resources.parse_version()` 函数智能得多。您应该利用哪个取决于您对版本字符串的期望程度。 (2认同)

Ant*_*sma 12

无需迭代版本元组.列表和元组上的内置比较运算符已经完全按照您的需要运行.您只需要将版本列表零扩展到相应的长度.使用python 2.6,您可以使用izip_longest填充序列.

from itertools import izip_longest
def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*izip_longest(parts1, parts2, fillvalue=0))
    return cmp(parts1, parts2)
Run Code Online (Sandbox Code Playgroud)

对于较低版本,需要一些地图hackery.

def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*map(lambda p1,p2: (p1 or 0, p2 or 0), parts1, parts2))
    return cmp(parts1, parts2)
Run Code Online (Sandbox Code Playgroud)


Pär*_*der 10

这比你的建议更紧凑.我没有用零填充较短的版本,而是在拆分后从版本列表中删除尾随零.

def normalize_version(v):
    parts = [int(x) for x in v.split(".")]
    while parts[-1] == 0:
        parts.pop()
    return parts

def mycmp(v1, v2):
    return cmp(normalize_version(v1), normalize_version(v2))
Run Code Online (Sandbox Code Playgroud)

  • +1 @jellybean:双线并不总是最好的维护和可读性,这个是同时非常清晰和紧凑的代码,此外,如果你需要,你可以在你的代码中重新使用`mycmp`用于其他目的它. (4认同)

yu_*_*sha 6

使用正则表达式删除尾随.0和.00,拆分并使用cmp函数正确比较数组.

def mycmp(v1,v2):
 c1=map(int,re.sub('(\.0+)+\Z','',v1).split('.'))
 c2=map(int,re.sub('(\.0+)+\Z','',v2).split('.'))
 return cmp(c1,c2)
Run Code Online (Sandbox Code Playgroud)

当然,如果你不介意长线,你可以将它转换为单线