python版本号排序_在Python中对版本排序

主要编辑:旧答案太不通俗。这里有两个更漂亮的解决方案。

所以,我现在看到了三种实现预定的方法,在实际发布之前发布候选“rc”。我以前的命令式排序

使用“b”而不是“rc”,以便使用来自同一个包的StrictVersion

扩展Version类以添加对任意标记和标记排序的支持

一。旧式命令式排序from distutils.version import LooseVersion

versions = ["1.7.0", "1.7.0.rc0", "1.8.0"]

lv = [LooseVersion(v) for v in versions]

lv.sort()

sorted_rc = [v.vstring for v in lv]

import re

p = re.compile('rc\\d+$')

i = 0

# skip the first RCs

while i + 1 < len(sorted_rc):

m = p.search(sorted_rc[i])

if m:

i += 1

else:

break

while i + 1 < len(sorted_rc):

tmp = sorted_rc[i]

m = p.search(sorted_rc[i+1])

if m and sorted_rc[i+1].startswith(tmp):

sorted_rc[i] = sorted_rc[i+1]

sorted_rc[i+1] = tmp

i += 1

有了这个我得到:['1.7.0rc0', '1.7.0', '1.11.0']

2。用“b”代替“rc”

如果允许您的1.7.0.rc0以1.7.0a0或1.7.0b0的形式写入,包distutils.version也有另一个类StrictVersion来执行任务,该类记录alpha或beta版本。

即:from distutils.version import StrictVersion

versions = ["1.7.0", "1.7.0b0", "1.11.0"]

sorted(versions, key=StrictVersion)

这就提供了:['1.7.0b0', '1.7.0', '1.11.0']

可以使用re模块完成从一个表单到另一个表单的转换。

三。扩展版本类

先前解决方案的明显问题是StrictVersion缺乏灵活性。更改version_re类属性以使用rc而不是a或b,即使它接受1.7.1rc0,仍然将其打印为1.7.1r0(从python 2.7.3开始)。

我们可以通过实现自己的自定义版本类来实现它。可以这样做,通过一些单元测试来确保正确性,至少在某些情况下:#!/usr/bin/python

# file: version2.py

from distutils import version

import re

import functools

@functools.total_ordering

class NumberedVersion(version.Version):

"""

A more flexible implementation of distutils.version.StrictVersion

This implementation allows to specify:

- an arbitrary number of version numbers:

not only '1.2.3' , but also '1.2.3.4.5'

- the separator between version numbers:

'1-2-3' is allowed when '-' is specified as separator

- an arbitrary ordering of pre-release tags:

1.1alpha3 < 1.1beta2 < 1.1rc1 < 1.1

when ["alpha", "beta", "rc"] is specified as pre-release tag list

"""

def __init__(self, vstring=None, sep='.', prerel_tags=('a', 'b')):

version.Version.__init__(self)

# super() is better here, but Version is an old-style class

self.sep = sep

self.prerel_tags = dict(zip(prerel_tags, xrange(len(prerel_tags))))

self.version_re = self._compile_pattern(sep, self.prerel_tags.keys())

self.sep_re = re.compile(re.escape(sep))

if vstring:

self.parse(vstring)

_re_prerel_tag = 'rel_tag'

_re_prerel_num = 'tag_num'

def _compile_pattern(self, sep, prerel_tags):

sep = re.escape(sep)

tags = '|'.join(re.escape(tag) for tag in prerel_tags)

if tags:

release_re = '(?:(?P{tags})(?P\d+))?'\

.format(tags=tags, tn=self._re_prerel_tag, nn=self._re_prerel_num)

else:

release_re = ''

return re.compile(r'^(\d+)(?:{sep}(\d+))*{rel}$'\

.format(sep=sep, rel=release_re))

def parse(self, vstring):

m = self.version_re.match(vstring)

if not m:

raise ValueError("invalid version number '{}'".format(vstring))

tag = m.group(self._re_prerel_tag)

tag_num = m.group(self._re_prerel_num)

if tag is not None and tag_num is not None:

self.prerelease = (tag, int(tag_num))

vnum_string = vstring[:-(len(tag) + len(tag_num))]

else:

self.prerelease = None

vnum_string = vstring

self.version = tuple(map(int, self.sep_re.split(vnum_string)))

def __repr__(self):

return "{cls} ('{vstring}', '{sep}', {prerel_tags})"\

.format(cls=self.__class__.__name__, vstring=str(self),

sep=self.sep, prerel_tags = list(self.prerel_tags.keys()))

def __str__(self):

s = self.sep.join(map(str,self.version))

if self.prerelease:

return s + "{}{}".format(*self.prerelease)

else:

return s

def __lt__(self, other):

"""

Fails when the separator is not the same or when the pre-release tags

are not the same or do not respect the same order.

"""

# TODO deal with trailing zeroes: e.g. "1.2.0" == "1.2"

if self.prerel_tags != other.prerel_tags or self.sep != other.sep:

raise ValueError("Unable to compare: instances have different"

" structures")

if self.version == other.version and self.prerelease is not None and\

other.prerelease is not None:

tag_index = self.prerel_tags[self.prerelease[0]]

other_index = self.prerel_tags[other.prerelease[0]]

if tag_index == other_index:

return self.prerelease[1] < other.prerelease[1]

return tag_index < other_index

elif self.version == other.version:

return self.prerelease is not None and other.prerelease is None

return self.version < other.version

def __eq__(self, other):

tag_index = self.prerel_tags[self.prerelease[0]]

other_index = other.prerel_tags[other.prerelease[0]]

return self.prerel_tags == other.prerel_tags and self.sep == other.sep\

and self.version == other.version and tag_index == other_index and\

self.prerelease[1] == other.prerelease[1]

import unittest

class TestNumberedVersion(unittest.TestCase):

def setUp(self):

self.v = NumberedVersion()

def test_compile_pattern(self):

p = self.v._compile_pattern('.', ['a', 'b'])

tests = {'1.2.3': True, '1a0': True, '1': True, '1.2.3.4a5': True,

'b': False, '1c0': False, ' 1': False, '': False}

for test, result in tests.iteritems():

self.assertEqual(result, p.match(test) is not None, \

"test: {} result: {}".format(test, result))

def test_parse(self):

tests = {"1.2.3.4a5": ((1, 2, 3, 4), ('a', 5))}

for test, result in tests.iteritems():

self.v.parse(test)

self.assertEqual(result, (self.v.version, self.v.prerelease))

def test_str(self):

tests = (('1.2.3',), ('10-2-42rc12', '-', ['rc']))

for t in tests:

self.assertEqual(t[0], str(NumberedVersion(*t)))

def test_repr(self):

v = NumberedVersion('1,2,3rc4', ',', ['lol', 'rc'])

expected = "NumberedVersion ('1,2,3rc4', ',', ['lol', 'rc'])"

self.assertEqual(expected, repr(v))

def test_order(self):

test = ["1.7.0", "1.7.0rc0", "1.11.0"]

expected = ['1.7.0rc0', '1.7.0', '1.11.0']

versions = [NumberedVersion(v, '.', ['rc']) for v in test]

self.assertEqual(expected, list(map(str,sorted(versions))))

if __name__ == '__main__':

unittest.main()

所以,可以这样使用:import version2

versions = ["1.7.0", "1.7.0rc2", "1.7.0rc1", "1.7.1", "1.11.0"]

sorted(versions, key=lambda v: version2.NumberedVersion(v, '.', ['rc']))

输出:['1.7.0rc1', '1.7.0rc2', '1.7.0', '1.7.1', '1.11.0']

因此,总之,使用python自带的电池或推出自己的电池。

关于这个实现:可以通过处理发行版中的尾随零来改进它,并记住正则表达式的编译。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值