对于这样一个记录纯数字版本号字符串的Python list:
versions_list = ["1.1.2", "1.0.0", "1.3.3", "1.0.12", "1.0.2"]
需要对其依据版本号大小进行排序.
相比于手动实现算法, 这里记录三种更tricky的方式.
本文的解题方法均来自于这个Stack Overflow问题: Sorting a list of dot-separated numbers, like software versions
使用 lambda
&map
首先介绍我觉得实现最优雅的函数式思维的解决办法.
我们知道Python list的sort()
方法可以接收一个参数key
. key
可以是一个函数, 对于list中的每一个元素, 都会在排序前对其执行key
所定义的函数, 将执行结果作为排序的依据. 而lambda允许我们定义一个匿名函数作为key
的值.
在此前提下, 我们可以想到, 如果将每一个版本号字符串, 如"1.1.2"
, 都转换成[1, 1, 2]
这样的[int]
格式, 这样一来, Python list的sort()
方法会自动根据版本号的各个位进行依次排序.
比如给定这样一个list l
:
l = [[1,1,2], [1,0,0], [1,3,3], [1,0,12], [1,0,2]]
对其直接使用sort()
方法:
l.sort()
print l
# 输出为
[[1, 0, 0], [1, 0, 2], [1, 0, 12], [1, 1, 2], [1, 3, 3]]
而由"1.1.2"
转换为[1, 1, 2]
的过程, 可以用map
函数来实现:
map(int, '1.1.2'.split('.'))
所以组合起来:
versions_list.sort(key=lambda s: map(int, s.split('.')))
将会输出排序后的版本号列表:
['1.0.0', '1.0.2', '1.0.12', '1.1.2', '1.3.3']
需要注意的是, 由于在Python3中, map
不再返回一个list, 所以我们需要在外面包一层list
方法:
versions_list.sort(key=lambda s: list(map(int, s.split('.'))))
如果很难理解map
的函数式思想, 也可以直接使用Python的for ... in ...
语法:
versions_list.sort(key=lambda s: [int(u) for u in s.split('.')])
这个解决方法使用到lambda
和map
, 可以说是相当Haskell了~
使用 distutils.version
包
对于Python这门语言, 我对它的印象一直是:
我能想到的日常需求, 肯定已经都有人帮我造好轮子了
不出所料地, Python标准库中的distutils.version
包, 可以轻易地帮助我们实现版本的排序:
from distutils.version import StrictVersion
versions = ["1.1.2", "1.0.0", "1.3.3", "1.0.12", "1.0.2"]
versions.sort(key=StrictVersion)
print versions
# 输出
['1.0.0', '1.0.2', '1.0.12', '1.1.2', '1.3.3']
甚至对于预发布的版本也不在话下:
versions = ["1.1", "1.1b1", "1.1a1"]
versions.sort(key=StrictVersion)
# 输出
["1.1a1", "1.1b1", "1.1"]
其他的使用方法可以参阅它的文档: https://github.com/python/cpython/blob/3.2/Lib/distutils/version.py#L101
使用natsort
包
类似地, 在Python3环境下, natsort
包也可以实现同样的需求:
from natsort import natsorted
versions = ["1.1.2", "1.0.0", "1.3.3", "1.0.12", "1.0.2"]
natsorted(versions)
print versions
# 输出
['1.0.0', '1.0.2', '1.0.12', '1.1.2', '1.3.3']
对于完整的版本字符串, natsort
也可以很好地完成排序:
versions = ['version-1.9', 'version-2.0', 'version-1.11', 'version-1.10']
natsorted(versions)
print versions
# 输出
['version-1.9', 'version-1.10', 'version-1.11', 'version-2.0']