比较Python中的版本string

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

我有一个正则expression式r"^(?P<eggName>\w+)-(?P<eggVersion>[\d\.]+)-.+\.egg$从文件名中提取名字和版本。问题是比较版本号,这是一个像2.3.1的string。

由于我比较string,2 10以上,但这是不正确的版本。

 >>> "2.3.1" > "10.1.1" True 

我可以做一些分裂,parsing,铸造等,我最终会得到一个解决方法。 但是这是Python, 而不是Java 。 有没有一种比较版本string的优雅方式?

使用distutils.versionpackaging.version.parse

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

两种select之间的区别:

  • distutils.version是内置的,但没有logging,只符合被取代的PEP 386 ;
  • packaging.version.parse是第三方实用程序,但由setuptools使用 (因此您可能已经安装了它),并且符合当前的PEP 440 ; 它也可以在单个函数中处理“松散”和“严格”版本(虽然“传统”版本总是会在有效版本之前进行sorting)。

由于distutils.version没有文档,下面是相关的文档(基于Python 3.3)供参考(从源代码中删除):

每个版本号类实现以下接口:

  • 'parse'方法接受一个string并将其parsing为一些内部表示; 如果string是一个无效的版本号,'parsing'引发一个ValueErrorexception
  • 类的构造函数接受一个可选的string参数,如果提供的话,传递给'parse'
  • __str__重build传递给“parse”的string(或等价的string – 即将生成等效版本号实例的string)
  • __repr__生成Python代码来重新创build版本号实例
  • _cmp将当前实例与同一个类的另一个实例或一个string(将被parsing为同一个类的实例,因此必须遵循相同的规则)

StrictVersion

肛门保留和软件理想主义者的版本号码。 如上所述实现版本号类的标准接口。 版本号由两个或三个以点分隔的数字组件组成,在结尾处具有可选的“预发布”标签。 预发布标签由字母“a”或“b”组成,后跟一个数字。 如果两个版本号的数字组成相等,则带有预发布标签的数字组件将始终被视为较早(较less),而不是一个。

以下是有效的版本号(以按照提供的cmp函数进行sorting的顺序显示):

 0.4 0.4.0 (these two are equivalent) 0.4.1 0.5a1 0.5b3 0.5 0.9.6 1.0 1.0.4a3 1.0.4b1 1.0.4 

以下是无效版本号的示例:

 1 2.7.2.2 1.3.a4 1.3pl1 1.3c4 

这个版本编号系统的基本原理将在distutils文档中解释。


LooseVersion

无政府主义者和软件现实主义者的版本编号。 如上所述实现版本号类的标准接口。 版本号由一系列数字组成,用句号或字母串隔开。 在比较版本号时,数字组件将在数字上进行比较,在字母上对字母组件进行比较。 以下是所有有效的版本号,顺序不限:

 1.5.1 1.5.2b2 161 3.10a 8.02 3.4j 1996.07.12 3.2.pl0 3.1.1.6 2g6 11g 0.960923 2.2beta29 1.13++ 5.5.kw 2.0b1pl0 

事实上,这个scheme根本就没有一个无效的版本号。 比较的规则是简单和可预测的,但可能并不总是给你想要的结果(对于“想要”的一些定义)。

setuptools定义了parse_version() 。 这实现了PEP 0440 – 版本识别 ,也能够parsing不遵循PEP的版本。 easy_installpip使用此函数来处理版本比较。 从文档 :

parsingPEP 440定义的项目版本string。返回的值将是表示版本的对象。 这些对象可以相互比较和sorting。 sortingalgorithm由PEP 440定义,除此之外,任何不是有效的PEP 440版本的版本将被认为小于任何有效的PEP 440版本,并且无效版本将继续使用原始algorithm进行sorting。

在PEP 440存在之前,所引用的“原始algorithm”在较早版本的文档中被定义。

在语义上,这种格式是distutils的StrictVersionLooseVersion类之间的粗略交叉; 如果你给它的版本,将与StrictVersion工作,那么他们将比较相同的方式。 否则,比较更像是一个“更智能”的LooseVersionforms。 有可能创build病态的版本编码scheme来欺骗这个parsing器,但在实践中它们应该是非常罕见的。

该文档提供了一些示例:

如果您想确定您select的编号scheme是按照您认为的方式工作的,则可以使用pkg_resources.parse_version()函数来比较不同的版本号:

 >>> from pkg_resources import parse_version >>> parse_version('1.9.a.dev') == parse_version('1.9a0dev') True >>> parse_version('2.1-rc2') < parse_version('2.1') True >>> parse_version('0.6a9dev-r41475') < parse_version('0.6a9') True 
 def versiontuple(v): return tuple(map(int, (v.split(".")))) >>> versiontuple("2.3.1") > versiontuple("10.1.1") False 

有包装包可用,这将允许您按照PEP-440 ,以及旧版本比较版本。

 >>> from packaging.version import Version, LegacyVersion >>> Version('1.1') < Version('1.2') True >>> Version('1.2.dev4+deadbeef') < Version('1.2') True >>> Version('1.2.8.5') <= Version('1.2') False >>> Version('1.2.8.5') <= Version('1.2.8.6') True 

传统版本支持:

 >>> LegacyVersion('1.2.8.5-5-gdeadbeef') <LegacyVersion('1.2.8.5-5-gdeadbeef')> 

将旧版本与PEP-440版本进行比较。

 >>> LegacyVersion('1.2.8.5-5-gdeadbeef') < Version('1.2.8.6') True 

将版本string转换成元组并将其从那里转换出来有什么问题? 对我来说似乎很优雅

 >>> (2,3,1) < (10,1,1) True >>> (2,3,1) < (10,1,1,1) True >>> (2,3,1,10) < (10,1,1,1) True >>> (10,3,1,10) < (10,1,1,1) False >>> (10,3,1,10) < (10,4,1,1) True 

@ kindall的解决scheme是代码看起来很好的一个简单例子。

基于Kindall的解决scheme发布我的全部function。 我能够通过填充每个版本部分的前导零来支持与数字混合的任何字母数字字符。

虽然不像他的单行函数那样漂亮,但它似乎可以很好地处理字母数字版本号。 (如果您的版本控制系统中有长string,请确保正确设置zfill(#)值。)

 def versiontuple(v): filled = [] for point in v.split("."): filled.append(point.zfill(8)) return tuple(filled) 

 >>> versiontuple("10a.4.5.23-alpha") > versiontuple("2a.4.5.23-alpha") True >>> "10a.4.5.23-alpha" > "2a.4.5.23-alpha" False 

阅读了semver.org上的语义版本编号之后。 我试着比较+符号。

版本3.6.0 + 1234应该与3.6.0相同。 只有pip包semver给出了正确的结果(python 2.7):

 import semver print semver.match('3.6.0+1234', '==3.6.0') # True from packaging.version import Version, LegacyVersion print LegacyVersion('3.6.0+1234') == LegacyVersion('3.6.0') # False from pkg_resources import parse_version print parse_version('3.6.0+1234') == parse_version('3.6.0') # False from distutils.version import LooseVersion, StrictVersion print LooseVersion('3.6.0+1234') == LooseVersion('3.6.0') # False 

我会做更多的select,做一个testing,使用LooseVersion,我得到我的testing第二大(可能是做一些事情,因为是我第一次使用该库)

 import itertools from distutils.version import LooseVersion, StrictVersion lista_de_frameworks = ["1.1.1", "1.2.5", "10.5.2", "3.4.5"] for a, b in itertools.combinations(lista_de_frameworks, 2): if LooseVersion(a) < LooseVersion(b): big = b print big list_test = [] for a in lista_de_frameworks: list_test.append( tuple(map(int, (a.split("."))))) print max(list_test) 

这就是我得到的:

3.4.5松散

(10,5,2),并带有tou </s>