source

Python 버전 번호는 어떻게 비교하나요?

factcode 2022. 11. 5. 08:51
반응형

Python 버전 번호는 어떻게 비교하나요?

이 들어 그 을 걀 에 추가하고 있다.sys.path같은 이 2개 버전만 디렉토리에 같은 .egg 버전이 2개 있는 경우 최신 버전만 추가하고 싶습니다.

을 하고 있어요.r"^(?P<eggName>\w+)-(?P<eggVersion>[\d\.]+)-.+\.egg$을 사용법입니다. 번호는 '버전 번호와 같은 입니다.버전 번호는 다음과 같은 문자열입니다.2.3.1.

스트링을 비교하고 있기 때문에 10 이상에서는 2개입니다만, 버전에서는 그렇지 않습니다.

>>> "2.3.1" > "10.1.1"
True

분할, 구문 분석, int에 캐스팅 등을 할 수 있고, 결국 해결 방법을 찾을 수 있었습니다.하지만 이것은 자바가 아닌 파이썬입니다.버전 문자열을 비교할 수 있는 우아한 방법이 있나요?

를 사용합니다.

>>> # pip install packaging
>>> 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, 「」가 있는 .packaging.version.LegacyVersion는 항상 됩니다.후자는 항상 유효한 버전보다 먼저 정렬됩니다.

주의: 패키지는 최근 셋톱툴에 판매되고 있습니다.


오래된 방법이지만 현재는 권장되지 않는 방법은 문서화되어 있지 않으며 대체된 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 버전은 "엄격하지 않다"고 생각되므로 유효한 버전이 무엇인지에 대한 현대의 Python의 개념과 일치하지 않습니다.

~로distutils.version문서화되어 있지 않습니다.다음은 관련 문서스트링입니다.

패키징 라이브러리에는 버전 기타 패키징 관련 기능을 사용하기 위한 유틸리티가 포함되어 있습니다.PEP 0440 -- Version Identification 을 실장하고, 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는 아이러니하게도 Import 시 많은 리소스를 사용하기 때문에 이 방법은 더 이상 권장되지 않습니다.그러나 모든 문서와 논의는 여전히 관련이 있습니다.

문서에서:

PEP 440에서 정의한 프로젝트 버전 문자열을 구문 분석했습니다.반환되는 값은 버전을 나타내는 개체입니다.이들 오브젝트를 서로 비교하고 정렬할 수 있습니다.정렬 알고리즘은 PEP 440에 의해 정의되며, 유효한 PEP 440 버전이 아닌 버전은 유효한 PEP 440 버전보다 작은 것으로 간주되며 비활성 버전은 원래 알고리즘을 사용하여 정렬을 계속합니다.

참조된 「오리지널 알고리즘」은, PEP 440이 존재하기 전의 낡은 버전의 문서에서 정의되고 있습니다.

형식은 의미론적으로 왜곡의 between과 between between의 입니다.StrictVersion ★★★★★★★★★★★★★★★★★」LooseVersionclass;와 함께 , 「」는 「」를 참조해 .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의 솔루션은 코드가 얼마나 좋은지 보여주는 간단한 예입니다.

setuptools 그 the the the the the? 이 제품은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

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

킨달의 솔루션을 기반으로 제 모든 기능을 게시합니다.각 버전 섹션을 선행 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

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

새로운 의존관계를 추가하지 않는 솔루션을 찾고 있었습니다.다음(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()

EDIT: 태플 비교와 함께 변형 추가.물론 태플 비교가 있는 변종이 더 좋지만 나는 정수 비교가 있는 변종을 찾고 있었다.

python을 사용하여 버전을 증가시키려면

def increment_version(version):
    version = version.split('.')
    if int(version[len(version) - 1]) >= 99:
        version[len(version) - 1] = '0'
        version[len(version) - 2] = str(int(version[len(version) - 2]) + 1)
    else:
        version[len(version) - 1] = str(int(version[len(version) - 1]) + 1)
    return '.'.join(version)

version = "1.0.0"
version_type_2 = "1.0"
print("old version",version ,"new version",increment_version(version))
print("old version",version_type_2 ,"new version",increment_version(version_type_2))

표준 strverscmp 및 Mark Byers의 이 솔루션과 유사하지만 빈 대소문자를 피하기 위해 분할 대신 findall을 사용합니다.

import re
num_split_re = re.compile(r'([0-9]+|[^0-9]+)')

def try_int(i, fallback=None):
    try:
        return int(i)
    except ValueError:
        pass
    except TypeError:
        pass
    return fallback

def ver_as_list(a):
    return [try_int(i, i) for i in num_split_re.findall(a)]

def strverscmp_lt(a, b):
    a_ls = ver_as_list(a)
    b_ls = ver_as_list(b)
    return a_ls < b_ls

시멘틱 버전이 "깨끗하다"고 가정하면 다음과 같이 동작합니다(예:x.x.x그리고 정렬해야 할 버전 목록이 있습니다.

# Here are some versions
versions = ["1.0.0", "1.10.0", "1.9.0"]

# This does not work
versions.sort() # Result: ['1.0.0', '1.10.0', '1.9.0']

# So make a list of tuple versions
tuple_versions = [tuple(map(int, (version.split(".")))) for version in versions]

# And sort the string list based on the tuple list
versions = [x for _, x in sorted(zip(tuple_versions, versions))] # Result: ['1.0.0', '1.9.0', '1.10.0']

최신 버전을 가져오려면 목록에서 마지막 요소를 선택하십시오.versions[-1]또는 를 사용하여 역순으로 정렬합니다.reverse의 특성sorted()로 설정하다True, 그리고,[0]요소.

물론 이 모든 것을 편리한 기능으로 포장하여 재사용할 수 있습니다.

def get_latest_version(versions):
    """
    Get the latest version from a list of versions.
    """
    try:
        tuple_versions = [tuple(map(int, (version.split(".")))) for version in versions]
        versions = [x for _, x in sorted(zip(tuple_versions, versions), reverse=True)]
        latest_version = versions[0]
    except Exception as e:
        print(e)
        latest_version = None

    return latest_version

print(get_latest_version(["1.0.0", "1.10.0", "1.9.0"]))

다시 쉽게 돌아가면...간단한 스크립트의 경우 다음을 사용할 수 있습니다.

import sys
needs = (3, 9) # or whatever
pvi = sys.version_info.major, sys.version_info.minor    

코드 뒷부분

try:
    assert pvi >= needs
except:
    print("will fail!")
    # etc.

언급URL : https://stackoverflow.com/questions/11887762/how-do-i-compare-version-numbers-in-python

반응형