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?

675voto

ecatmur Punkte 145884

使用する packaging.version.parse .

>>> # 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 ist ein Dienstprogramm eines Drittanbieters, wird aber von setuptools (Sie haben es also wahrscheinlich bereits installiert) und entspricht dem aktuellen PEP 440 ; es wird ein packaging.version.Version wenn die Version konform ist und eine packaging.version.LegacyVersion wenn nicht. Letztere werden immer vor den gültigen Versionen sortiert.

Hinweis : Die Verpackung wurde kürzlich in Setuptools verkauft .


Eine alte und jetzt veraltet Methode, auf die Sie stoßen könnten, ist distutils.version ist sie undokumentiert und entspricht nur dem überholten 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'

Wie Sie sehen können, sieht es gültige PEP 440-Versionen als "nicht strikt" an und entspricht daher nicht der modernen Python-Vorstellung davon, was eine gültige Version ist.

Als distutils.version ist undokumentiert, aquí sind die entsprechenden Dokumentationsstrings.

135voto

davidism Punkte 110299

Le Verpackung Bibliothek enthält Dienstprogramme für Arbeiten mit Versionen und andere verpackungsbezogene Funktionen. Dies implementiert PEP 0440 -- Versionskennzeichnung und ist auch in der Lage, Versionen zu parsen, die nicht dem PEP folgen. Es wird von pip und anderen gängigen Python-Tools zum Parsen und Vergleichen von Versionen verwendet.

$ pip install packaging

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

Dieser wurde vom ursprünglichen Code in setuptools und pkg_resources abgetrennt, um ein leichteres und schnelleres Paket zu erstellen.


Bevor es die Paketierungsbibliothek gab, war (und ist) diese Funktionalität in pkg_resources zu finden, einem Paket, das von setuptools bereitgestellt wird. Dies wird jedoch nicht mehr bevorzugt, da die Installation von setuptools nicht mehr gewährleistet ist (es gibt andere Paketierungswerkzeuge) und pkg_resources ironischerweise ziemlich viele Ressourcen verbraucht, wenn es importiert wird. Dennoch sind alle Dokumente und Diskussionen immer noch relevant.

Von der parse_version() docs :

Analysiert den Versionsstring eines Projekts, wie in PEP 440 definiert. Der Rückgabewert ist ein Objekt, das die Version darstellt. Diese Objekte können miteinander verglichen und sortiert werden. Der Sortieralgorithmus entspricht der Definition von PEP 440 mit dem Zusatz, dass jede Version, die keine gültige PEP 440-Version ist, als kleiner als jede gültige PEP 440-Version betrachtet wird und die ungültigen Versionen weiterhin nach dem ursprünglichen Algorithmus sortiert werden.

Der "ursprüngliche Algorithmus", auf den verwiesen wird, wurde in älteren Versionen der Dokumente definiert, bevor es PEP 440 gab.

Semantisch ist das Format eine grobe Kreuzung zwischen distutils' StrictVersion y LooseVersion Klassen; wenn Sie ihm Versionen geben, die mit StrictVersion dann werden sie auf die gleiche Weise vergleichen. Ansonsten sind Vergleiche eher eine "intelligentere" Form von LooseVersion . Es ist möglich, pathologische Versionskodierungsschemata zu erstellen, die diesen Parser täuschen, aber sie sollten in der Praxis sehr selten sein.

Le Dokumentation liefert einige Beispiele:

Wenn Sie sicher sein wollen, dass das von Ihnen gewählte Nummerierungsschema funktioniert so funktioniert, wie Sie es sich vorstellen, können Sie die pkg_resources.parse_version() Funktion, um verschiedene Versionsnummern zu vergleichen:

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

85voto

kindall Punkte 167554
def versiontuple(v):
    return tuple(map(int, (v.split("."))))

>>> versiontuple("2.3.1") > versiontuple("10.1.1")
False

33voto

Gabi Purcaru Punkte 29912

Was ist falsch daran, die Versionszeichenfolge in ein Tupel umzuwandeln und von dort aus weiterzugehen? Scheint für mich elegant genug

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

Die Lösung von @kindall ist ein schnelles Beispiel dafür, wie gut der Code aussehen würde.

20voto

Die Art und Weise, wie setuptools tut es, es verwendet die pkg_resources.parse_version Funktion. Es sollte sein PEP440 willfährig.

Beispiel:

#! /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

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