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&#61;None, sep&#61;&#39;.&#39;, prerel_tags&#61;(&#39;a&#39;, &#39;b&#39;)):
version.Version.__init__(self)
# super() is better here, but Version is an old-style class
self.sep &#61; sep
self.prerel_tags &#61; dict(zip(prerel_tags, xrange(len(prerel_tags))))
self.version_re &#61; self._compile_pattern(sep, self.prerel_tags.keys())
self.sep_re &#61; re.compile(re.escape(sep))
if vstring:
self.parse(vstring)
_re_prerel_tag &#61; &#39;rel_tag&#39;
_re_prerel_num &#61; &#39;tag_num&#39;
def _compile_pattern(self, sep, prerel_tags):
sep &#61; re.escape(sep)
tags &#61; &#39;|&#39;.join(re.escape(tag) for tag in prerel_tags)
if tags:
release_re &#61; &#39;(?:(?P{tags})(?P\d&#43;))?&#39;\
.format(tags&#61;tags, tn&#61;self._re_prerel_tag, nn&#61;self._re_prerel_num)
else:
release_re &#61; &#39;&#39;
return re.compile(r&#39;^(\d&#43;)(?:{sep}(\d&#43;))*{rel}$&#39;\
.format(sep&#61;sep, rel&#61;release_re))
def parse(self, vstring):
m &#61; self.version_re.match(vstring)
if not m:
raise ValueError("invalid version number &#39;{}&#39;".format(vstring))
tag &#61; m.group(self._re_prerel_tag)
tag_num &#61; m.group(self._re_prerel_num)
if tag is not None and tag_num is not None:
self.prerelease &#61; (tag, int(tag_num))
vnum_string &#61; vstring[:-(len(tag) &#43; len(tag_num))]
else:
self.prerelease &#61; None
vnum_string &#61; vstring
self.version &#61; tuple(map(int, self.sep_re.split(vnum_string)))
def __repr__(self):
return "{cls} (&#39;{vstring}&#39;, &#39;{sep}&#39;, {prerel_tags})"\
.format(cls&#61;self.__class__.__name__, vstring&#61;str(self),
sep&#61;self.sep, prerel_tags &#61; list(self.prerel_tags.keys()))
def __str__(self):
s &#61; self.sep.join(map(str,self.version))
if self.prerelease:
return s &#43; "{}{}".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" &#61;&#61; "1.2"
if self.prerel_tags !&#61; other.prerel_tags or self.sep !&#61; other.sep:
raise ValueError("Unable to compare: instances have different"
" structures")
if self.version &#61;&#61; other.version and self.prerelease is not None and\
other.prerelease is not None:
tag_index &#61; self.prerel_tags[self.prerelease[0]]
other_index &#61; self.prerel_tags[other.prerelease[0]]
if tag_index &#61;&#61; other_index:
return self.prerelease[1] return tag_index elif self.version &#61;&#61; other.version:
return self.prerelease is not None and other.prerelease is None
return self.version def __eq__(self, other):
tag_index &#61; self.prerel_tags[self.prerelease[0]]
other_index &#61; other.prerel_tags[other.prerelease[0]]
return self.prerel_tags &#61;&#61; other.prerel_tags and self.sep &#61;&#61; other.sep\
and self.version &#61;&#61; other.version and tag_index &#61;&#61; other_index and\
self.prerelease[1] &#61;&#61; other.prerelease[1]
import unittest
class TestNumberedVersion(unittest.TestCase):
def setUp(self):
self.v &#61; NumberedVersion()
def test_compile_pattern(self):
p &#61; self.v._compile_pattern(&#39;.&#39;, [&#39;a&#39;, &#39;b&#39;])
tests &#61; {&#39;1.2.3&#39;: True, &#39;1a0&#39;: True, &#39;1&#39;: True, &#39;1.2.3.4a5&#39;: True,
&#39;b&#39;: False, &#39;1c0&#39;: False, &#39; 1&#39;: False, &#39;&#39;: 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 &#61; {"1.2.3.4a5": ((1, 2, 3, 4), (&#39;a&#39;, 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 &#61; ((&#39;1.2.3&#39;,), (&#39;10-2-42rc12&#39;, &#39;-&#39;, [&#39;rc&#39;]))
for t in tests:
self.assertEqual(t[0], str(NumberedVersion(*t)))
def test_repr(self):
v &#61; NumberedVersion(&#39;1,2,3rc4&#39;, &#39;,&#39;, [&#39;lol&#39;, &#39;rc&#39;])
expected &#61; "NumberedVersion (&#39;1,2,3rc4&#39;, &#39;,&#39;, [&#39;lol&#39;, &#39;rc&#39;])"
self.assertEqual(expected, repr(v))
def test_order(self):
test &#61; ["1.7.0", "1.7.0rc0", "1.11.0"]
expected &#61; [&#39;1.7.0rc0&#39;, &#39;1.7.0&#39;, &#39;1.11.0&#39;]
versions &#61; [NumberedVersion(v, &#39;.&#39;, [&#39;rc&#39;]) for v in test]
self.assertEqual(expected, list(map(str,sorted(versions))))
if __name__ &#61;&#61; &#39;__main__&#39;:
unittest.main()
所以&#xff0c;可以这样使用&#xff1a;import version2
versions &#61; ["1.7.0", "1.7.0rc2", "1.7.0rc1", "1.7.1", "1.11.0"]
sorted(versions, key&#61;lambda v: version2.NumberedVersion(v, &#39;.&#39;, [&#39;rc&#39;]))
输出&#xff1a;[&#39;1.7.0rc1&#39;, &#39;1.7.0rc2&#39;, &#39;1.7.0&#39;, &#39;1.7.1&#39;, &#39;1.11.0&#39;]
因此&#xff0c;总之&#xff0c;使用python自带的电池或推出自己的电池。
关于这个实现&#xff1a;可以通过处理发行版中的尾随零来改进它&#xff0c;并记住正则表达式的编译。