339

卵を含むディレクトリを歩いて、それらの卵を に追加していsys.pathます。ディレクトリに同じ .egg のバージョンが 2 つある場合、最新のものだけを追加したい。

r"^(?P<eggName>\w+)-(?P<eggVersion>[\d\.]+)-.+\.egg$ファイル名から名前とバージョンを抽出する正規表現があります。問題は、バージョン番号を比較することです。これは、のような文字列です2.3.1

文字列を比較しているので、10 を超えると 2 ソートになりますが、バージョンの場合は正しくありません。

>>> "2.3.1" > "10.1.1"
True

分割、解析、int へのキャストなどを行うことができ、最終的には回避策が得られます。しかし、これはJava ではなくPython です。バージョン文字列を比較するエレガントな方法はありますか?

4

13 に答える 13

536

を使用しpackaging.version.parseます。

>>> 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によって使用され(おそらくすでにインストールされています)、現在のPEP440に準拠しています。packaging.version.Versionバージョンが準拠している場合はaを返し、準拠してpackaging.version.LegacyVersionいない場合はを返します。後者は常に有効なバージョンの前にソートされます。

:パッケージは最近setuptoolsに組み込まれました。


あなたが遭遇するかもしれない古くて現在非推奨の方法は、文書化されておらず、置き換えられたPEP386distutils.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'

ご覧のとおり、有効なPEP 440バージョンは「厳密ではない」と見なされるため、有効なバージョンが何であるかという最新のPythonの概念とは一致しません。

distutils.version文書化されていないように、ここに関連するdocstringがあります。

于 2012-08-09T16:30:03.290 に答える
123

パッケージライブラリには、バージョンやその他のパッケージ関連機能を操作するためのユーティリティが含まれています。これは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 は皮肉なことに、インポート時にかなりのリソースを使用します。ただし、すべてのドキュメントと議論は依然として関連しています。

parse_version()ドキュメントから:

PEP 440 で定義されているように、プロジェクトのバージョン文字列を解析しました。返される値は、バージョンを表すオブジェクトになります。これらのオブジェクトは、互いに比較して並べ替えることができます。ソートアルゴリズムは PEP 440 で定義されている通りですが、有効な PEP 440 バージョンではないバージョンは有効な PEP 440 バージョンよりも小さいと見なされ、無効なバージョンは元のアルゴリズムを使用してソートを続行します。

参照されている「元のアルゴリズム」は、PEP 440 が存在する前の古いバージョンのドキュメントで定義されていました。

意味的には、フォーマットは distutilsStrictVersionLooseVersionクラスの間の大雑把なクロスです。で動作するバージョンを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
于 2014-01-11T17:36:50.983 に答える
67
def versiontuple(v):
    return tuple(map(int, (v.split("."))))

>>> versiontuple("2.3.1") > versiontuple("10.1.1")
False
于 2012-08-09T16:26:40.293 に答える
24

バージョン文字列をタプルに変換してそこから移動することの何が問題になっていますか? 私には十分にエレガントに見えます

>>> (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 のソリューションは、コードの見栄えの簡単な例です。

于 2012-08-09T16:26:27.943 に答える
10

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
于 2015-02-12T18:38:29.757 に答える
7

semverパッケージを使用して、バージョンがセマンティック バージョンの要件を満たしているかどうかを判断できます。これは、2 つの実際のバージョンを比較することと同じではありませんが、一種の比較です。

たとえば、バージョン 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
于 2017-09-26T12:48:43.657 に答える