[python] 파이썬에서 버전 번호를 어떻게 비교합니까?

에 계란을 추가하기 위해 계란이 들어있는 디렉토리를 걷고 있습니다 sys.path. 디렉토리에 동일한 .egg의 두 가지 버전이있는 경우 최신 버전 만 추가하고 싶습니다.

r"^(?P<eggName>\w+)-(?P<eggVersion>[\d\.]+)-.+\.egg$파일 이름에서 이름과 버전을 추출 하는 정규식이 있습니다 . 문제는 버전 번호를 비교하는 것 2.3.1입니다.

문자열을 비교하고 있기 때문에 2는 10보다 높지만 버전에는 맞지 않습니다.

>>> "2.3.1" > "10.1.1"
True

분할, 파싱, int로 캐스팅 등을 할 수 있었고 결국 해결 방법을 얻었습니다. 그러나 이것은 Java가 아닌 Python 입니다. 버전 문자열을 비교할 수있는 우아한 방법이 있습니까?



답변

사용하십시오 packaging.version.parse.

>>> 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
>>> isinstance(version.parse("1.3.a4"), version.Version)
True
>>> isinstance(version.parse("1.3.xy123"), version.LegacyVersion)
True
>>> version.Version("1.3.xy123")
Traceback (most recent call last):
...
packaging.version.InvalidVersion: Invalid version: '1.3.xy123'

packaging.version.parse타사 유틸리티이지만 setuptools에서 사용 하므로 (아마도 이미 설치되어 있음) 현재 PEP 440을 준수합니다 . packaging.version.Version버전이 호환 되는 경우 a를 반환하고 그렇지 않은 경우 a를 반환합니다 packaging.version.LegacyVersion. 후자는 항상 유효한 버전보다 먼저 정렬됩니다.

참고 : 패키징은 최근 에 setuptools공급되었습니다 .


많은 소프트웨어가 여전히 사용하는 고대의 대안은 distutils.version내장되어 있지만 문서화되지 않고 대체 된 PEP 386 에만 적합합니다 .

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

보시다시피 유효한 PEP 440 버전은“엄격하지 않은”것으로 보이므로 현대 파이썬의 유효한 버전에 대한 개념과 일치하지 않습니다.

으로 distutils.version문서화되고, 여기에 ‘관련 문서화 문자열을이야.


답변

포장 라이브러리를위한 유틸리티가 포함되어 버전 작업 및 기타 포장 관련 기능을 제공합니다. 이것은 PEP 0440-버전 식별을 구현 하며 PEP를 따르지 않는 버전을 구문 분석 할 수도 있습니다. pip 및 기타 일반적인 Python 도구에서 버전 구문 분석 및 비교를 제공하는 데 사용됩니다.

$ pip install packaging
from packaging.version import parse as parse_version
version = parse_version('1.0.3.dev')

setuptools 및 pkg_resources의 원래 코드에서 분리되어보다 가볍고 빠른 패키지를 제공합니다.


패키징 라이브러리가 존재하기 전에이 기능은 setuptools가 제공하는 패키지 인 pkg_resources에서 찾을 수있었습니다. 그러나 setuptools가 더 이상 설치되도록 보장하지 않으므로 (다른 패키징 도구가 존재 함) pkg_resources는 아이러니하게도 가져올 때 많은 자원을 사용합니다. 그러나 모든 문서와 토론은 여전히 ​​관련이 있습니다.

로부터 parse_version()문서 :

PEP 440에 정의 된대로 프로젝트의 버전 문자열을 구문 분석했습니다. 반환 된 값은 버전을 나타내는 객체입니다. 이 객체들은 서로 비교되고 분류 될 수 있습니다. 정렬 알고리즘은 유효한 PEP 440 버전이 아닌 모든 버전이 유효한 PEP 440 버전보다 낮은 것으로 간주되고 유효하지 않은 버전은 원래 알고리즘을 사용하여 정렬을 계속한다는 점 외에 PEP 440에 의해 정의 된대로입니다.

참조 된 “원래 알고리즘”은 PEP 440이 존재하기 전에 이전 버전의 문서에서 정의되었습니다.

의미 적으로, 형식은 distutils StrictVersionLooseVersion클래스 사이의 대략적인 교차입니다 . 와 작동하는 버전을 제공 StrictVersion하면 동일한 방식으로 비교됩니다. 그렇지 않으면 비교는 “스마트 한”형식과 비슷합니다 LooseVersion. 이 파서를 속이는 병적 인 버전 코딩 체계를 만들 수는 있지만 실제로는 드 물어야합니다.

문서 는 몇 가지 예를 제공합니다.

선택한 번호 체계가 원하는 방식으로 작동하는지 확인하려면이 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


답변

버전 문자열을 튜플로 변환하고 거기에서 나가는 것은 무엇이 문제입니까? 나에게 충분히 우아해 보인다

>>> (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의 솔루션은 코드가 얼마나 잘 보이는지에 대한 간단한 예입니다.


답변

포장 이 따라 버전을 비교 할 수 있도록 가능한 패키지, 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


답변

semver 패키지를 사용하여 버전이 시맨틱 버전 요구 사항을 충족하는지 판별 할 수 있습니다 . 이것은 두 개의 실제 버전을 비교하는 것과 동일하지 않지만 비교 유형입니다.

예를 들어, 버전 3.6.0 + 1234는 3.6.0과 같아야합니다.

import semver
semver.match('3.6.0+1234', '==3.6.0')
# True

from packaging import version
version.parse('3.6.0+1234') == version.parse('3.6.0')
# False

from distutils.version import LooseVersion
LooseVersion('3.6.0+1234') == LooseVersion('3.6.0')
# False


답변

Kindall의 솔루션을 기반으로 내 모든 기능을 게시합니다. 각 버전 섹션을 선행 0으로 채워 숫자와 혼합 된 모든 영숫자를 지원할 수있었습니다.

확실히 한 줄짜리 함수만큼 예쁘지는 않지만 영숫자 버전 번호와 잘 작동하는 것 같습니다. 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