[python] Python의 버전 번호 비교

내가 쓰고 싶은 cmp두 가지 버전 번호 및 수익률을 비교 -like 기능을 -1, 0또는 1자신의 비교 valuses에 따라.

  • -1버전 A가 버전 B보다 오래된 경우 반환
  • 0버전 A와 B가 동일한 경우 반환
  • 1버전 A가 버전 B보다 새로운 경우 반환

각 하위 섹션은 숫자로 해석되므로 1.10> 1.1입니다.

원하는 기능 출력은 다음과 같습니다.

mycmp('1.0', '1') == 0
mycmp('1.0.0', '1') == 0
mycmp('1', '1.0.0.1') == -1
mycmp('12.10', '11.0.0.0.0') == 1
...

그리고 여기에 내 구현이 있으며 개선을 위해 열려 있습니다.

def mycmp(version1, version2):
    parts1 = [int(x) for x in version1.split('.')]
    parts2 = [int(x) for x in version2.split('.')]

    # fill up the shorter version with zeros ...
    lendiff = len(parts1) - len(parts2)
    if lendiff > 0:
        parts2.extend([0] * lendiff)
    elif lendiff < 0:
        parts1.extend([0] * (-lendiff))

    for i, p in enumerate(parts1):
        ret = cmp(p, parts2[i])
        if ret: return ret
    return 0

Python 2.4.5 btw를 사용하고 있습니다. (내 작업장에 설치 …).

다음은 사용할 수있는 작은 ‘테스트 모음’입니다.

assert mycmp('1', '2') == -1
assert mycmp('2', '1') == 1
assert mycmp('1', '1') == 0
assert mycmp('1.0', '1') == 0
assert mycmp('1', '1.000') == 0
assert mycmp('12.01', '12.1') == 0
assert mycmp('13.0.1', '13.00.02') == -1
assert mycmp('1.1.1.1', '1.1.1.1') == 0
assert mycmp('1.1.1.2', '1.1.1.1') == 1
assert mycmp('1.1.3', '1.1.3.000') == 0
assert mycmp('3.1.1.0', '3.1.2.10') == -1
assert mycmp('1.1', '1.10') == -1



답변

문자열에서 흥미롭지 않은 부분 (후행 0과 점)을 제거한 다음 숫자 목록을 비교합니다.

import re

def mycmp(version1, version2):
    def normalize(v):
        return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
    return cmp(normalize(version1), normalize(version2))

이것은 Pär Wieslander와 동일한 접근 방식이지만 좀 더 간결합니다.

다음은 ” Bash에서 점으로 구분 된 버전 형식의 두 문자열을 비교하는 방법? ” 덕분에 몇 가지 테스트입니다 .

assert mycmp("1", "1") == 0
assert mycmp("2.1", "2.2") < 0
assert mycmp("3.0.4.10", "3.0.4.2") > 0
assert mycmp("4.08", "4.08.01") < 0
assert mycmp("3.2.1.9.8144", "3.2") > 0
assert mycmp("3.2", "3.2.1.9.8144") < 0
assert mycmp("1.2", "2.1") < 0
assert mycmp("2.1", "1.2") > 0
assert mycmp("5.6.7", "5.6.7") == 0
assert mycmp("1.01.1", "1.1.1") == 0
assert mycmp("1.1.1", "1.01.1") == 0
assert mycmp("1", "1.0") == 0
assert mycmp("1.0", "1") == 0
assert mycmp("1.0", "1.0.1") < 0
assert mycmp("1.0.1", "1.0") > 0
assert mycmp("1.0.2.0", "1.0.2") == 0


답변

파이썬을 사용하는 것은 distutils.version.StrictVersion어떻습니까?

>>> from distutils.version import StrictVersion
>>> StrictVersion('10.4.10') > StrictVersion('10.4.9')
True

따라서 귀하의 cmp기능 :

>>> cmp = lambda x, y: StrictVersion(x).__cmp__(y)
>>> cmp("10.4.10", "10.4.11")
-1

더 복잡한 버전 번호를 비교하려는 경우 distutils.version.LooseVersion더 유용하지만 동일한 유형 만 비교해야합니다.

>>> from distutils.version import LooseVersion, StrictVersion
>>> LooseVersion('1.4c3') > LooseVersion('1.3')
True
>>> LooseVersion('1.4c3') > StrictVersion('1.3')  # different types
False

LooseVersion 가장 지능적인 도구가 아니며 쉽게 속일 수 있습니다.

>>> LooseVersion('1.4') > LooseVersion('1.4-rc1')
False

이 품종에서 성공하려면 표준 라이브러리에서 벗어나 setuptools 의 구문 분석 유틸리티를 사용해야 합니다 parse_version.

>>> from pkg_resources import parse_version
>>> parse_version('1.4') > parse_version('1.4-rc2')
True

따라서 특정 사용 사례에 따라 기본 제공 distutils도구가 충분한 지 또는 종속성으로 추가 해야하는지 여부를 결정해야합니다 setuptools.


답변

이 경우 재사용 은 우아함으로 간주됩니까? 🙂

# pkg_resources is in setuptools
# See http://peak.telecommunity.com/DevCenter/PkgResources#parsing-utilities
def mycmp(a, b):
    from pkg_resources import parse_version as V
    return cmp(V(a),V(b))


답변

버전 튜플을 반복 할 필요가 없습니다. 목록과 튜플에 내장 된 비교 연산자는 이미 원하는대로 작동합니다. 버전 목록을 해당 길이로 0으로 확장하면됩니다. 파이썬 2.6에서는 izip_longest를 사용하여 시퀀스를 채울 수 있습니다.

from itertools import izip_longest
def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*izip_longest(parts1, parts2, fillvalue=0))
    return cmp(parts1, parts2)

낮은 버전에서는 일부지도 해킹이 필요합니다.

def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*map(lambda p1,p2: (p1 or 0, p2 or 0), parts1, parts2))
    return cmp(parts1, parts2)


답변

이것은 당신의 제안보다 조금 더 간결합니다. 짧은 버전을 0으로 채우는 대신 분할 후 버전 목록에서 후행 0을 제거합니다.

def normalize_version(v):
    parts = [int(x) for x in v.split(".")]
    while parts[-1] == 0:
        parts.pop()
    return parts

def mycmp(v1, v2):
    return cmp(normalize_version(v1), normalize_version(v2))


답변

후행 .0.00정규식을 제거 하고 배열을 올바르게 비교 split하는 cmp함수를 사용하십시오 .

def mycmp(v1,v2):
 c1=map(int,re.sub('(\.0+)+\Z','',v1).split('.'))
 c2=map(int,re.sub('(\.0+)+\Z','',v2).split('.'))
 return cmp(c1,c2)

물론 긴 줄이 마음에 들면 한 줄로 변환 할 수 있습니다.


답변

def compare_version(v1, v2):
    return cmp(*tuple(zip(*map(lambda x, y: (x or 0, y or 0),
           [int(x) for x in v1.split('.')], [int(y) for y in v2.split('.')]))))

하나의 라이너입니다 (가독성을 위해 분할). 읽을 수 있는지 잘 모르겠습니다 …