326 votes

Comment comparer "version" de style chaînes

Je suis en train de marcher un répertoire qui contient les oeufs ajouter les oeufs à la Pythonpath (sys.chemin d'accès). Si il y a deux versions de la même chose .l'oeuf dans le répertoire, je tiens à ajouter que le dernier.

J'ai créer une expression régulière simple (r"^(?P<eggName>\w+)-(?P<eggVersion>[\d\.]+)-.+\.egg$) pour extraire le nom et la version de l'oeuf. L'idée est de garder l'information dans un dictionnaire et une fois que l'ensemble du répertoire a été parcouru, en ajoutant seulement la version la plus récente des œufs. Garder la trace de l'œuf fichiers/répertoires à ajouter n'est pas la question. Le problème est de comparer le numéro de version, qui est une chaîne de caractères comme... "2.3.1".

Depuis que je suis de la comparaison de chaînes, ce qui se passe:

>>> "2.3.1" > "10.1.1"
True

Mais la version 2 n'est pas une version supérieure à la version 10.

Je sais que je pourrais toujours commencer à faire certaines fractionnement, l'analyse, la conversion en int, yadda, yadda, bla... Et j'allais finir par trouver une solution de contournement, mais... il y a à être plus propre, plus efficace (et élégant) de la comparaison. Est-il? C'est Python, pas de Java... il y a un moyen élégant de faire ce genre de chose...?

Aussi, le but de tout cela est l'ajout de la droite oeufs au Pythonpath alors peut-être que je pourrais aller avec une approche totalement différente en utilisant une sorte de... d'œufs gestionnaire de paquet? Ce genre de réponses sont également les bienvenus.

Je vous remercie à l'avance.

505voto

ecatmur Points 64173

Utiliser distutils.version.

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

Notez que LooseVersion et StrictVersion ont été dépréciées en vertu de PEP 386 et à un certain point être remplacé par NormalizedVersion.


Comme le Python docs sont vides, voici pertinentes docstrings (basé sur Python 3.3) de référence (entaillé à partir de la source):

Chaque numéro de version de la classe implémente l'interface suivante:

  • les "analyser" la méthode prend une chaîne et l'analyse à l'interne la représentation; si la chaîne n'est pas valide numéro de version, 'analyser' soulève ValueError d'exception
  • le constructeur de classe prend une chaîne facultative, argument qui, s'il est fourni, est passé à 'analyser'
  • __str__ reconstitue la chaîne qui a été adoptée à "analyser" (ou une chaîne équivalente -- ie. celui qui va générer un équivalent numéro de version de l'instance)
  • __repr__ génère du code Python à recréer le numéro de version de l'instance
  • _cmp compare la situation actuelle avec une autre instance de la même classe ou d'une chaîne de caractères (qui sera analysé à une instance de la même classe, donc doit suivre les mêmes règles)

StrictVersion

La numérotation de Version pour anal retentives et logiciel idéalistes. Implémente l'interface standard pour numéro de version de classes décrit ci-dessus. Un numéro de version se compose de deux ou trois séparé par des points numériques des composants, avec une option "pré-version" tag sur la fin. La pré-version du tag est constitué de la lettre 'a' ou 'b' suivi par un nombre. Si le numérique composants de la version deux nombres sont égaux, alors avec une version pré-balise de toujours réputé plus tôt (inférieures) à l'autre sans.

Les suivants sont valides numéros de version (illustrée dans l'ordre serait obtenu par tri en fonction du cmp fonction):

0.4       0.4.0  (these two are equivalent)
0.4.1
0.5a1
0.5b3
0.5
0.9.6
1.0
1.0.4a3
1.0.4b1
1.0.4

Les exemples suivants sont des non valide les numéros de version:

1
2.7.2.2
1.3.a4
1.3pl1
1.3c4

La justification de cette version du système de numérotation sera expliqué dans le distutils.


LooseVersion

La numérotation de Version pour les anarchistes et les logiciels réalistes. Implémente l'interface standard pour numéro de version de classes décrit ci-dessus. Un numéro de version se compose d'une série de nombres, séparés par des périodes ou des chaînes de lettres. Lors de la comparaison les numéros de version, numérique de composants par rapport numériquement, et le classement alphabétique des composants lexicalement. La suite valides sont les numéros de version, dans aucun ordre particulier:

1.5.1
1.5.2b2
161
3.10a
8.02
3.4j
1996.07.12
3.2.pl0
3.1.1.6
2g6
11g
0.960923
2.2beta29
1.13++
5.5.kw
2.0b1pl0

En fait, il n'y a pas une telle chose comme un numéro de version non valide en vertu de l' ce schéma; les règles sont simple et prévisible, mais ne donne pas toujours les résultats que vous voulez (pour une définition des "veux").

122voto

davidism Points 5946

setuptools définit parse_version(). Ce qui semble être la plus robuste, ainsi que la manière utilisée par easy_install et pip. À partir de l' API docs:

Analyser un projet de chaîne de version et de retourner une valeur qui peut être utilisé pour comparer les versions par ordre chronologique. Sémantiquement, le format est une ébauche de croisement entre distutils' StrictVersion et LooseVersion classes; si vous lui donnez des versions qui travaillerait avec des StrictVersion, ensuite, ils pourront comparer de la même manière. Sinon, les comparaisons sont plus comme un "plus intelligent" forme d' LooseVersion. Il est possible de créer pathologique version des schémas de codage qui sera fou cet analyseur, mais ils doivent être très rares dans la pratique.

Plus de la documentation:

Si vous voulez être certain que votre choisi un système de numérotation des travaux de la façon dont vous jugez, vous pouvez utiliser l' pkg_resources.parse_version() fonction pour comparer les différents numéros de 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

65voto

kindall Points 60645
def versiontuple(v):
    return tuple(map(int, (v.split("."))))

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

21voto

Gabi Purcaru Points 15158

Quel est le problème avec la transformation de la chaîne de version dans un n-uplet et va à partir de là? Semble assez élégant pour moi

>>> (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 de la solution est un exemple rapide de la qualité du code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X