파이썬에서 버전 번호를 어떻게 비교합니까?
에 계란을 추가하기 위해 계란이 들어있는 디렉토리를 걷고 있습니다
sys.path
. 디렉토리에 같은 .egg의 두 가지 버전이있는 경우 최신 버전 만 추가하고 싶습니다.
r"^(?P<eggName>\w+)-(?P<eggVersion>[\d\.]+)-.+\.egg$
파일 이름에서 이름과 버전을 추출 하는 정규식이 있습니다 . 문제는 버전 번호를 비교하는 것
2.3.1
입니다.문자열을 비교하고 있기 때문에 2는 10보다 높지만 버전에는 맞지 않습니다.
>>> "2.3.1" > "10.1.1"
True
분할, 파싱, int로 캐스팅 등을 할 수 있었고 결국 해결 방법을 얻었습니다. 그러나 이것은
Python 입니다. 버전 문자열을 비교할 수있는 우아한 방법이 있습니까?
사용하십시오
.
>>> 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
타사 유틸리티이지만
사용 하므로 (아마도 이미 설치되어 있음) 현재
준수합니다 .
packaging.version.Version
버전이 호환 되는 경우 a를 반환하고 그렇지 않은 경우 a를 반환합니다
packaging.version.LegacyVersion
. 후자는 항상 유효한 버전보다 먼저 정렬됩니다.
많은 소프트웨어가 여전히 사용하는 고대의 대안은
내장되어 있지만 문서화되지 않았으며 대체 된
에만 적합합니다 .
>>> 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
문서화되고,
'관련 문서화 문자열을이야.
setuptools가 정의합니다
parse_version()
. 이것은
구현 하며 PEP를 따르지 않는 버전을 구문 분석 할 수도 있습니다. 이 기능에 의해 사용되는
easy_install
및
핸들 버전을 비교합니다. 로부터
:
PEP 440에 정의 된대로 프로젝트의 버전 문자열을 구문 분석했습니다. 반환 된 값은 버전을 나타내는 객체입니다. 이 객체들은 서로 비교되고 분류 될 수 있습니다. 정렬 알고리즘은 유효한 PEP 440 버전이 아닌 모든 버전이 유효한 PEP 440 버전보다 낮은 것으로 간주되고 유효하지 않은 버전은 원래 알고리즘을 사용하여 정렬을 계속한다는 점 외에 PEP 440에 의해 정의 된대로입니다.
참조 된 "원래 알고리즘"은 PEP 440이 존재하기 전에 이전 버전의 문서에서 정의되었습니다.
의미 적으로, 형식은 distutils와
StrictVersion
클래스 사이의 대략적인 교차입니다 . 와 작동하는 버전을 제공
LooseVersion
하면 동일한 방식으로 비교됩니다. 그렇지 않으면 비교는 "스마트 한"형식과 비슷합니다
StrictVersion
. 이 파서를 속이는 병적 인 버전 코딩 체계를 만들 수는 있지만 실제로는 드 rare니다.
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
setuptools를 사용하지 않는 경우
프로젝트 는이 패키지 및 기타 패키징 관련 기능을 별도의 라이브러리로 분할합니다.
from packaging import version
version.parse('1.0.3.dev')
from pkg_resources import parse_version
parse_version('1.0.3.dev')
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의 솔루션은 코드가 얼마나 잘 보이는지에 대한 간단한 예입니다.
이
이 따라 버전을 비교 할 수 있도록 가능한 패키지,
뿐 아니라 기존 버전.
>>> 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
패키지를 사용하여 버전이
요구 사항을 충족하는지 판별 할 수 있습니다 . 이것은 두 개의 실제 버전을 비교하는 것과 동일하지 않지만 비교 유형입니다.예를 들어, 버전 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
그렇게하는 방식으로 함수를
setuptools
사용합니다
pkg_resources.parse_version
.
PEP440을
준수 해야합니다 .예:
#! /usr/bin/python
# -*- coding: utf-8 -*-
"""Example comparing two PEP440 formatted versions
"""
import pkg_resources
VERSION_A = pkg_resources.parse_version("1.0.1-beta.1")
VERSION_B = pkg_resources.parse_version("v2.67-rc")
VERSION_C = pkg_resources.parse_version("2.67rc")
VERSION_D = pkg_resources.parse_version("2.67rc1")
VERSION_E = pkg_resources.parse_version("1.0.0")
print(VERSION_A)
print(VERSION_B)
print(VERSION_C)
print(VERSION_D)
print(VERSION_A==VERSION_B) #FALSE
print(VERSION_B==VERSION_C) #TRUE
print(VERSION_C==VERSION_D) #FALSE
print(VERSION_A==VERSION_E) #FALSE
새로운 종속성을 추가하지 않는 솔루션을 찾고있었습니다. 다음 (Python 3) 솔루션을 확인하십시오.
class VersionManager:
@staticmethod
def compare_version_tuples(
major_a, minor_a, bugfix_a,
major_b, minor_b, bugfix_b,
):
"""
Compare two versions a and b, each consisting of 3 integers
(compare these as tuples)
version_a: major_a, minor_a, bugfix_a
version_b: major_b, minor_b, bugfix_b
:param major_a: first part of a
:param minor_a: second part of a
:param bugfix_a: third part of a
:param major_b: first part of b
:param minor_b: second part of b
:param bugfix_b: third part of b
:return: 1 if a > b
0 if a == b
-1 if a < b
"""
tuple_a = major_a, minor_a, bugfix_a
tuple_b = major_b, minor_b, bugfix_b
if tuple_a > tuple_b:
return 1
if tuple_b > tuple_a:
return -1
return 0
@staticmethod
def compare_version_integers(
major_a, minor_a, bugfix_a,
major_b, minor_b, bugfix_b,
):
"""
Compare two versions a and b, each consisting of 3 integers
(compare these as integers)
version_a: major_a, minor_a, bugfix_a
version_b: major_b, minor_b, bugfix_b
:param major_a: first part of a
:param minor_a: second part of a
:param bugfix_a: third part of a
:param major_b: first part of b
:param minor_b: second part of b
:param bugfix_b: third part of b
:return: 1 if a > b
0 if a == b
-1 if a < b
"""
# --
if major_a > major_b:
return 1
if major_b > major_a:
return -1
# --
if minor_a > minor_b:
return 1
if minor_b > minor_a:
return -1
# --
if bugfix_a > bugfix_b:
return 1
if bugfix_b > bugfix_a:
return -1
# --
return 0
@staticmethod
def test_compare_versions():
functions = [
(VersionManager.compare_version_tuples, "VersionManager.compare_version_tuples"),
(VersionManager.compare_version_integers, "VersionManager.compare_version_integers"),
]
data = [
# expected result, version a, version b
(1, 1, 0, 0, 0, 0, 1),
(1, 1, 5, 5, 0, 5, 5),
(1, 1, 0, 5, 0, 0, 5),
(1, 0, 2, 0, 0, 1, 1),
(1, 2, 0, 0, 1, 1, 0),
(0, 0, 0, 0, 0, 0, 0),
(0, -1, -1, -1, -1, -1, -1), # works even with negative version numbers :)
(0, 2, 2, 2, 2, 2, 2),
(-1, 5, 5, 0, 6, 5, 0),
(-1, 5, 5, 0, 5, 9, 0),
(-1, 5, 5, 5, 5, 5, 6),
(-1, 2, 5, 7, 2, 5, 8),
]
count = len(data)
index = 1
for expected_result, major_a, minor_a, bugfix_a, major_b, minor_b, bugfix_b in data:
for function_callback, function_name in functions:
actual_result = function_callback(
major_a=major_a, minor_a=minor_a, bugfix_a=bugfix_a,
major_b=major_b, minor_b=minor_b, bugfix_b=bugfix_b,
)
outcome = expected_result == actual_result
message = "{}/{}: {}: {}: a={}.{}.{} b={}.{}.{} expected={} actual={}".format(
index, count,
"ok" if outcome is True else "fail",
function_name,
major_a, minor_a, bugfix_a,
major_b, minor_b, bugfix_b,
expected_result, actual_result
)
print(message)
assert outcome is True
index += 1
# test passed!
if __name__ == '__main__':
VersionManager.test_compare_versions()
편집 : 튜플 비교 변형을 추가했습니다. 물론 튜플 비교가있는 변형이 더 좋지만 정수 비교가있는 변형을 찾고있었습니다.
참고URL : https://stackoverflow.com/questions/11887762/how-do-i-compare-version-numbers-in-python
'programing' 카테고리의 다른 글
PIL 이미지를 numpy 배열로 변환하는 방법은 무엇입니까? (0) | 2020.05.09 |
---|---|
AWS EC2 인스턴스를 핑할 수 없습니다 (0) | 2020.05.09 |
JAXB에 의해 생성 된 @XmlRootElement가 없습니다. (0) | 2020.05.09 |
MySQL의 유형 : BigInt (20) vs Int (20) (0) | 2020.05.09 |
Rails 4 진품 토큰 (0) | 2020.05.09 |