我正在浏览一个包含卵的目录,以便将这些卵添加到sys.path。如果目录中有相同的.egg的两个版本,我只想添加最新的版本。

我有一个正则表达式r ^ (? P < eggName > \ w +) - (? P < eggVersion > (\ d \.]+)-.+\.Egg $从文件名中提取名称和版本。问题是比较版本号,它是一个像2.3.1这样的字符串。

因为我比较字符串,2排序超过10,但这是不正确的版本。

>>> "2.3.1" > "10.1.1"
True

我可以做一些拆分、解析、转换为int等,最终我将得到一个变通方法。但这是Python,不是Java。是否有一种优雅的方法来比较版本字符串?


当前回答

使用packaging.version.parse。

>>> # pip install packaging
>>> from packaging import version
>>> version.parse("2.3.1") < version.parse("10.1.2")
True
>>> version.parse("1.3.a4") < version.parse("10.1.2")
True
>>> isinstance(version.parse("1.3.a4"), version.Version)
True
>>> isinstance(version.parse("1.3.xy123"), version.LegacyVersion)
True
>>> version.Version("1.3.xy123")
Traceback (most recent call last):
...
packaging.version.InvalidVersion: Invalid version: '1.3.xy123'

package .version.parse是一个第三方实用程序,但被setuptools使用(所以你可能已经安装了它),并且符合当前的PEP 440;如果版本兼容,它将返回package .version. version,如果不兼容,则返回package .version. legacyversion。后者总是在有效版本之前排序。

注意:setuptools最近提供了打包。


您可能遇到的一种古老且现在已弃用的方法是distutils。版本,它是无文档的,只符合取代的PEP 386;

>>> from distutils.version import LooseVersion, StrictVersion
>>> LooseVersion("2.3.1") < LooseVersion("10.1.2")
True
>>> StrictVersion("2.3.1") < StrictVersion("10.1.2")
True
>>> StrictVersion("1.3.a4")
Traceback (most recent call last):
...
ValueError: invalid version number '1.3.a4'

正如您所看到的,它将有效的PEP 440版本视为“不严格”,因此不符合现代Python对有效版本的定义。

distutils。版本没有文档,这里是相关的文档字符串。

其他回答

setuptools的方法是使用pkg_resources。parse_version函数。它应该符合PEP440标准。

例子:

#! /usr/bin/python
# -*- coding: utf-8 -*-
"""Example comparing two PEP440 formatted versions
"""
import pkg_resources

VERSION_A = pkg_resources.parse_version("1.0.1-beta.1")
VERSION_B = pkg_resources.parse_version("v2.67-rc")
VERSION_C = pkg_resources.parse_version("2.67rc")
VERSION_D = pkg_resources.parse_version("2.67rc1")
VERSION_E = pkg_resources.parse_version("1.0.0")

print(VERSION_A)
print(VERSION_B)
print(VERSION_C)
print(VERSION_D)

print(VERSION_A==VERSION_B) #FALSE
print(VERSION_B==VERSION_C) #TRUE
print(VERSION_C==VERSION_D) #FALSE
print(VERSION_A==VERSION_E) #FALSE
def versiontuple(v):
    return tuple(map(int, (v.split("."))))

>>> versiontuple("2.3.1") > versiontuple("10.1.1")
False

... 回到简单的话题… 对于简单的脚本,您可以使用:

import sys
needs = (3, 9) # or whatever
pvi = sys.version_info.major, sys.version_info.minor    

在代码的后面

try:
    assert pvi >= needs
except:
    print("will fail!")
    # etc.

假设你的语义版本是“干净的”(例如,x.x.x),并且你有一个需要排序的版本列表,这里有一些东西可以工作。

# Here are some versions
versions = ["1.0.0", "1.10.0", "1.9.0"]

# This does not work
versions.sort() # Result: ['1.0.0', '1.10.0', '1.9.0']

# So make a list of tuple versions
tuple_versions = [tuple(map(int, (version.split(".")))) for version in versions]

# And sort the string list based on the tuple list
versions = [x for _, x in sorted(zip(tuple_versions, versions))] # Result: ['1.0.0', '1.9.0', '1.10.0']

要获得最新版本,您只需选择列表版本[-1]中的最后一个元素,或者使用sorted()的reverse属性进行反向排序,将其设置为True,并获得[0]元素。

当然,您可以将所有这些打包到一个方便的函数中以供重用。

def get_latest_version(versions):
    """
    Get the latest version from a list of versions.
    """
    try:
        tuple_versions = [tuple(map(int, (version.split(".")))) for version in versions]
        versions = [x for _, x in sorted(zip(tuple_versions, versions), reverse=True)]
        latest_version = versions[0]
    except Exception as e:
        print(e)
        latest_version = None

    return latest_version

print(get_latest_version(["1.0.0", "1.10.0", "1.9.0"]))

简单的few-liner:

import sys
if (sys.version_info.major, sys.version_info.minor) >= (3, 9):
    ...
else:
    ...