430 Stimmen

Wie kann ich Versionsnummern in Python vergleichen?

Ich gehe durch ein Verzeichnis, das Eier enthält, um diese Eier zu den sys.path . Wenn es zwei Versionen desselben .egg im Verzeichnis gibt, möchte ich nur die neueste hinzufügen.

Ich habe einen regulären Ausdruck r"^(?P<eggName>\w+)-(?P<eggVersion>[\d\.]+)-.+\.egg$ um den Namen und die Version aus dem Dateinamen zu extrahieren. Das Problem besteht darin, die Versionsnummer zu vergleichen, die eine Zeichenkette ist wie 2.3.1 .

Da ich Zeichenketten vergleiche, wird 2 über 10 sortiert, aber das ist für Versionen nicht korrekt.

>>> "2.3.1" > "10.1.1"
True

Ich könnte einige Splitting, Parsing, Casting zu int, etc. tun, und ich würde schließlich eine Problemlösung zu bekommen. Aber das ist Python, nicht Java . Gibt es eine elegante Möglichkeit, Versionszeichenfolgen zu vergleichen?

13voto

sashk Punkte 3737

Hay Verpackung Paket zur Verfügung, mit dem Sie die Versionen gemäß der PEP-440 als auch ältere Versionen.

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

Unterstützung älterer Versionen:

>>> LegacyVersion('1.2.8.5-5-gdeadbeef')
<LegacyVersion('1.2.8.5-5-gdeadbeef')>

Vergleich der alten Version mit der PEP-440-Version.

>>> LegacyVersion('1.2.8.5-5-gdeadbeef') < Version('1.2.8.6')
True

11voto

Phaxmohdem Punkte 441

Ich poste meine vollständige Funktion, die auf der Lösung von Kindall basiert. Ich konnte alle alphanumerischen Zeichen, die mit den Zahlen vermischt sind, unterstützen, indem ich jeden Versionsabschnitt mit führenden Nullen auffüllte.

Sie ist zwar nicht so hübsch wie seine Einzeiler-Funktion, aber sie scheint gut mit alphanumerischen Versionsnummern zu funktionieren. (Stellen Sie nur sicher, dass Sie die zfill(#) Wert entsprechend anpassen, wenn Sie in Ihrem Versionierungssystem lange Zeichenfolgen haben).

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

9voto

Prikkeldraad Punkte 1327

Sie können die semver Paket, um festzustellen, ob eine Version eine semantische Version Anforderung. Dies ist nicht dasselbe wie der Vergleich zweier tatsächlicher Versionen, aber eine Art von Vergleich.

Die Version 3.6.0+1234 sollte zum Beispiel die gleiche sein wie 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

1voto

Stefan Saru Punkte 1605

Ich habe nach einer Lösung gesucht, die keine neuen Abhängigkeiten hinzufügt. Sehen Sie sich die folgende (Python 3) Lösung an:

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: Variante mit Tupelvergleich hinzugefügt. Natürlich ist die Variante mit Tupel-Vergleich schöner, aber ich war auf der Suche nach der Variante mit Integer-Vergleich

1voto

Galuoises Punkte 1717

Wenn Sie einen Filter für eine Bibliotheksversion erstellen möchten, können Sie die Funktion __version__ Attribut (hier ein Beispiel mit der jwt-Bibliothek):

from packaging import version
import jwt

if version.parse(jwt.__version__) < version.parse('2.0.0'):
    # TODO: your code

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X