メインコンテンツへスキップ
見出し画像

uvの使い方完全ガイド|Python環境構築と既存プロジェクト移行の実例【競馬AI開発#55】

    uvは、Astral社が開発したPython統合ツールで、これまでpyenv・venv・pip・pip-toolsと複数に分かれていたPython環境構築の作業を、たった1つのコマンドで扱えるようにしたものです。

    画像
    https://docs.astral.sh/uv/ より
    他のパッケージマネージャと比べて高速であることがアピールされている

    本記事ではまず、uvの基本的な使い方から、既存のPythonプロジェクトをuv管理に移行する具体的な手順について解説します。
    後半では実例として、この【競馬AI開発】シリーズで扱っているプロジェクトをuv管理へ切り替える方法を解説していきます。

    1. 基本コマンド早見表

    まず最初に、「uvコマンドの使い方」だけ手っ取り早く探しに来た人のため、コマンド早見表を先に貼っておきます。これさえ手元にあれば、日々の開発でよく使うuvコマンドは一旦網羅しているはず。これらのコマンドが何をしているのかは、次章以降で具体的に解説していきます。

    ■環境の構築・実行

    uv sync                         # pyproject.toml と uv.lock から .venv を構築
    uv run python script.py         # .venv の中でスクリプトを実行(activate不要)
    uv run pytest                   # pytest を .venv で実行

    ■依存関係の編集

    uv add pandas                   # パッケージを追加(pyproject.toml と uv.lock を自動更新)
    uv add 'pandas==2.2.2'          # バージョンを指定して追加
    uv remove pandas                # 削除(間接依存も自動クリーンアップ)

    ■ロックファイルの操作

    uv lock                           # pyproject.toml から uv.lock を再生成
    uv lock --upgrade                 # 全依存を最新版にアップグレード
    uv lock --upgrade-package pandas  # pandas だけ最新に

    ■Pythonバージョン管理

    uv python install 3.13.7          # Python 3.13.7 をインストール
    uv python pin 3.13.7              # プロジェクトで使うPythonを固定
    uv python list                    # 利用可能なPython一覧

    ■グローバルツール

    uv tool install black             # グローバルCLIツールをインストール
    uvx black .                       # CLIツールを一度だけ実行(インストール不要)
    uv self update                    # uv 本体のアップデート

    1.1. uvのインストール方法

    OSごとにコマンドを選んで、ターミナル(またはPowerShell)で一度だけ実行します。

    # macOS / Linux(推奨)
    curl -LsSf https://astral.sh/uv/install.sh | sh
    # Windows (PowerShell)
    powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
    # macOS で Homebrew 派の場合
    brew install uv

    インストール後、ターミナルを開き直してからバージョンを確認します。

    uv --version
    # 例: uv 0.8.23

    もし‍uv: command not found‍と出る場合は、‍~/.local/bin‍にPATHが通っていません。‍~/.zshrc‍や‍~/.bashrc‍に次の1行を追記して再読込してください。

    export PATH="$HOME/.local/bin:$PATH"

    アンインストールは‍uv self uninstall‍、アップデートは‍uv self update‍で行います。

    2. uvとは? Python環境構築の課題と、uvで変わること

    そもそも、なぜuvのような新しいツールが必要なのか。
    この章では、Pythonの環境構築にまつわる課題と、uvがそれをどう解決しているかを整理していきます。

    2.1. 従来のPython環境構築が抱える4つの課題

    Pythonで開発を始めると、多くの人が以下のような問題に直面します。

    • Python本体のバージョン衝突:ライブラリAはPython 3.11を要求、ライブラリBはPython 3.13を要求するなど、1台のマシンに複数のPythonを共存させる必要が出てきます。python3コマンドがどの実体を指すかは上書きされやすく、「さっきまで動いていたのに動かない」という事故が起きる可能性があります。

    • グローバル汚染:pip install pandasと打つと、デフォルトではシステム全体のPythonにインストールされます。プロジェクトAがpandas==2.0、プロジェクトBがpandas==2.2を要求した場合、どちらか片方しか満たせずぶつかります。これを依存関係の衝突と呼びます。

    • 依存関係の地獄:各パッケージは別のパッケージに依存しており、たとえばlightgbm → numpy、numpy → scipy、scipy → numpy…と、数十〜数百のパッケージが互いに要求を出し合う構造になっています。成立するバージョンの組み合わせを人間が手動で管理するのはかなり骨が折れます。

    • 再現性の欠如:「自分のマシンでは動いたのに、他人のマシンでは動かない」問題。pip installを打った時期が違えば届くバージョンも違い、OSやPythonマイナーバージョンの違いも絡んできます。データ分析・機械学習系のプロジェクトでは特に致命的な問題です。

    これらを解決するため、歴史的にはPythonエコシステムでは役割ごとに別々のツールが発達してきました。

    画像

    しかし、それぞれが別ツールであるために、覚えるコマンドが多い、activateやpip freezeの忘れが頻発する、依存解決が素朴で遅い、といった運用上の痛みが残っていました。

    実際、この【競馬AI開発】シリーズでも同様の課題が発生していました。
    このプロジェクトでは、完成した競馬AIのソースコードを配布し、アップデートがあれば反映させているのですが、配布先の環境でも再現性をもって動くように設計するには、かなり神経を使う必要がありました。
    必要なライブラリをまとめたrequirements.txtを併せて配布しているものの、アップデートに伴って新たなライブラリを追加する際には忘れずにrequirements.txtにも追記する必要があるのと、.venvは配布できないのでpythonの環境構築は「バージョンだけ指定するので、これに合わせてよしなに構築してください」という形で、各自に任せる形をとっていました。

    ‍2.2. uvのメリット

    uvは、これまで別々のツールに分かれていた以下の役割をすべて1つのコマンドで扱えるようにしたものです。

    画像

    公式サイトのキャッチコピーは以下の通りで、pipの10〜100倍速で動くことを売りにしています。

    An extremely fast Python package and project manager, written in Rust. 10-100x faster than pip.
    (Rustで書かれた、極めて高速なPythonパッケージ兼プロジェクトマネージャ。pipより10〜100倍高速。)

    uvの設計の核となる考え方は、次の3つです。

    • 統合(unification)‍:分散していた役割を1つにまとめ、覚えるべきコマンドを最小化する。

    • 速度(speed)‍:Rust製で並列化を徹底しており、pipの10〜100倍高速に動作する。

    • 再現性(reproducibility)‍:すべての操作が‍pyproject.toml‍と‍uv.lock‍に記録され、誰のマシンでも完全に同じ環境を再現できる。

    従来のpyenv + venv + pipフローとuvの違いを比較すると次のようになります。

    画像

    簡単に言うと「uvコマンド一発で全てが済んで楽だし、処理もめちゃくちゃ速い」という感じです。

    3. uvの基本的な使い方

    ここからは、uvを実際に使うときのファイル構成とコマンドを詳しく見ていきます。この章までを読めば、新規プロジェクトを立ち上げてuvで管理する一通りの流れが理解できます。

    3.1. uvのインストール

    OSごとにコマンドを選んで、ターミナル(またはPowerShell)で一度だけ実行します。

    # macOS / Linux(推奨)
    curl -LsSf https://astral.sh/uv/install.sh | sh
    # Windows (PowerShell)
    powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
    # macOS で Homebrew 派の場合
    brew install uv

    インストール後、ターミナルを開き直してからバージョンを確認します。

    uv --version
    # 例: uv 0.8.23

    もし‍uv: command not foundと出る場合は、‍~/.local/binにPATHが通っていません。‍~/.zshrcや‍~/.bashrcに次の1行を追記して再読込してください。

    export PATH="$HOME/.local/bin:$PATH"

    アンインストールが必要になったら以下でOKです。

    uv self uninstall

    uv自体のアップデートは次のコマンドで行います。

    uv self update

    3.2. uvが管理する4つのファイル・ディレクトリ

    uvを使うプロジェクトでは、次の4つのファイル・ディレクトリが中心的な役割を担います。ここを押さえれば、uvの全体像が見通せるようになります。

    myproject/
    ├── pyproject.toml       ← 買い物リスト(人間が編集)
    ├── uv.lock              ← レシート(uvが自動生成)
    ├── .python-version      ← 使うPythonバージョン(1行)
    └── .venv/               ← 隔離された実行空間(.gitignoreで除外)

    ‍■pyproject.toml
    プロジェクトで必要なパッケージを宣言するファイル。Python公式標準(PEP 621)に準拠しています。

    [project]
    name = "myproject"
    version = "0.1.0"
    requires-python = "==3.13.13"
    dependencies = [
        "pandas>=2.2.3",
        "lightgbm>=4.5.0",
    ]

    「これが欲しい」とゆるく宣言するファイルで、人間が手で編集します。

    ‍■uv.lock
    uvがpyproject.tomlを解析し、依存関係を解決した結果として‍「実際に採用されたバージョン一覧」‍を自動生成するファイル。人間は手で編集しません。

    • 直接依存だけでなく間接依存までのすべてのバージョンを記録

    • ハッシュ値(SHA256)‍も記録するので、改ざん検知ができる

    • macOS / Linux / Windowsすべてのビルド情報を1ファイルに保持

    たとえるなら、pyproject.tomlが「りんご3個」という買い物リストなら、uv.lockは「青森県産ふじ、1個180円、3個で540円」まで書いたレシートみたいなイメージです。

    ‍■.python-version
    以下のような1行だけのファイル。

    3.13.7

    uvはこのファイルを見て「このプロジェクトは3.13.7を使う」と判断し、なければ自動でダウンロードしてくれます(pyenv不要)。pyenv時代からの伝統的フォーマットで互換性があります。

    ‍■.venv/
    ‍uv syncを実行するとプロジェクト直下に自動生成される仮想環境。中身は従来のpython -m venv .venvと同じ形式なので、必要ならsource .venv/bin/activateで入ることもできます。

    • プロジェクト専用のPython実体とパッケージが入る

    • システムのPythonとは完全に隔離される

    これら4つのファイル・ディレクトリのGit管理の要否をまとめると次のようになります。

    ファイル/ディレクトリ   Git管理      理由
    ──────────────────────────────────────────────────────────────
    pyproject.toml          ○        プロジェクトの定義そのもの
    uv.lock                 ○        全員の環境を一致させるため
    .python-version         ○        使うPythonバージョンを揃えるため
    .venv/                  ×        各自のマシンで再生成すれば良い

    3.3. uvの主要コマンド

    uvのコマンドはたくさんありますが、主に使うのは以下の5つです。それぞれを順に解説していきます。

    ‍■uv sync — 環境を再現する
    「uv.lockの通りの環境を.venv/に作って」というコマンド。

    uv sync

    内部では以下が自動で行われます。

    1. .python-versionを読み、指定バージョンのPythonがなければダウンロード

    2. .venv/がなければ作成

    3. uv.lockに記載されたパッケージをすべて並列でダウンロード・展開

    4. ハッシュを検証し、.venv/に配置

    既に環境が正しければ何もせずに終わります。

    従来ツールで同じことをやろうとすると、次のようなコマンドが必要でした。

    # 従来
    pyenv install 3.11.4
    pyenv local 3.11.4
    python3 -m venv .venv
    source .venv/bin/activate
    pip install -r requirements.txt

    これが‍uv syncたった1行に圧縮されるわけです。

    ‍■uv run — 作った環境で実行する
    「.venv/の中のコマンドをそのまま実行して」というコマンド。

    uv run python script.py
    uv run pytest
    uv run jupyter lab

    内部では以下が行われます。

    1. .venv/がなければuv sync相当を自動実行

    2. .venv/bin/にPATHを通した一時的なサブプロセスを立ち上げる

    3. その中で指定されたコマンドを実行

    ここで押さえるべきポイントは、従来のsource .venv/bin/activateとの違いです。activateは現在のシェル全体に影響を与え続けるので、他のプロジェクトでも.venvがアクティブ化されたままになり、事故の原因になっていました。一方、uv runはそのコマンドの実行中だけ環境を提供する仕組みなので、「activateを忘れる」事故が構造的に起きません。

    ‍■uv add / uv remove — 依存関係の編集

    uv add pandas                 # 最新を追加
    uv add 'pandas==2.2.2'        # バージョン指定
    uv remove pandas              # 削除

    この1回のコマンドで、以下がすべて実行されます。

    1. pyproject.tomlを書き換え

    2. 依存解決を再計算

    3. uv.lockを更新

    4. .venv/に実際にインストール(または削除)

    pyproject.tomlやuv.lockを自動更新してくれる点が個人的にはかなり気に入っています。従来の「requirements.txtの更新忘れ事故」が、原理的に発生しなくなります。

    ‍■uv lock — ロックファイルの再計算

    uv lock                           # pyproject.toml → uv.lock の再計算
    uv lock --upgrade                 # 全パッケージを最新版にアップグレード
    uv lock --upgrade-package pandas  # 特定パッケージだけ最新に

    uv lockは.venv/には触らないので、更新後はuv syncで反映します。

    ‍■uv python — Pythonバージョン管理

    uv python install 3.13.7          # 明示的にインストール
    uv python list                    # 使えるPython一覧
    uv python pin 3.13.7              # .python-version を書き換える

    OSやCPUアーキテクチャに合ったものが自動で選ばれます。

    3.4. uv pipコマンド(補足)

    uvには‍上記で紹介したような「uv本来のコマンド群(uv add・uv sync・uv run)」‍のほかに‍「pip互換のコマンド群(uv pip install・uv pip freeze)」‍も存在します。

    画像

    uv pip系は‍「既存のpip運用からuvに段階移行するための橋渡し」‍と位置づけられており、速度は速いものの、pyproject.tomlやuv.lockには記録が残らない点に注意が必要です。公式でも「pipの完全な複製ではない」と明言されています。

    新規プロジェクトでは最初からuv addベースに揃え、既存プロジェクトを移行する場合も最終的にはpyproject.toml + uv.lock運用へ寄せるのがおすすめです。

    4. requirements.txtからuvに移行する手順(実践)

    ここからは、既存のrequirements.txt管理のPythonプロジェクトを、uv管理に移行する具体的な手順を解説していきます。例として取り上げるのは、本シリーズで開発している競馬予想AI「keibaAI-v3」の実際の移行作業です。

    ‍■本プロジェクトの競馬AIについて
    この【競馬AI開発】シリーズでは、netkeiba.comから収集したデータを機械学習モデルに学習させることで期待リターンを計算し、リターンが最大化するように自動投票を行う競馬予想AIを開発しています。

    すでにシステムとしては一通り完成しており、現在はこの完成したシステムをベースに、特徴量の追加やモデルの改善など、さらなる精度向上に取り組んでいる段階です。

    このシリーズでは、こうしたシステムをゼロから構築する過程を、コード付きで詳しく解説しています。

    4.1. 具体的な移行手順

    はじめに、今回の移行で何を変えるかを押さえておきます。

    ‍■変えるもの
    ・ライブラリの管理方法を requirements.txt → pyproject.toml + uv.lock へ
    ・実行コマンドを ‍python3 script.py → ‍uv run python script.py へ
    ■変えないもの
    ・ソースコード本体やディレクトリ構成

    実際に、uv管理へ移行していきましょう。

    ‍■ステップ1:uvをインストールする
    まだインストールしていない場合は、前章で紹介したようにOSに合わせて1回だけインストールします。

    # macOS / Linux
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
    # Windows (PowerShell)
    powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

    インストール後、ターミナルを開き直してから‍uv --versionで確認します。

    ‍■ステップ2:.python-versionを作る
    プロジェクトで使うPythonバージョンを1行で記します。uvはこのファイルを自動で読み、必要なら対応するPythonを自動ダウンロードします(pyenv不要)。

    echo "3.11.4" > .python-version

    または‍uvのコマンドでも同じことができます。

    uv python pin 3.11.4
    cat .python-version
    # 3.11.4

    ‍■ステップ3:pyproject.tomlを記入する
    次に、プロジェクトのルートにpyproject.tomlを作ります。

    注意点として、公式チュートリアルにはuv initで雛形を作る手順が紹介されていますが、既存プロジェクトに対しては使わないでください。理由は以下の通りです。

    • uv initはsrc/<project_name>/__init__.pyのようなパッケージ雛形を勝手に作る

    • 既存のmodules/やsrc/といったディレクトリ構造と衝突する

    • ‍[build-system]‍を自動で入れてしまい、「パッケージ化モード」として振る舞う

    keibaAI-v3のような「ライブラリではなくアプリケーション」では、‍[build-system]‍を書かないアプリケーションモードで扱うのが正解です。

    既存のrequirements.txtは次のような内容でした。

    ‍▼keibaAI-v3/requirements.txt(移行前)‍

    apscheduler==3.11.0
    beautifulsoup4==4.12.3
    betacal==1.1.0
    lightgbm==4.5.0
    lxml==5.4.0
    matplotlib==3.8.2
    numpy==1.26.4
    pandas==2.2.2
    playwright==1.49.1
    pytest==8.3.5
    python-dotenv==1.0.0
    PyYAML==6.0.1
    scikit_learn==1.4.0
    seaborn==0.13.2
    selenium==4.23.1
    tqdm==4.66.1
    webdriver_manager==4.0.2

    これをpyproject.tomlの‍[project].dependenciesに移します。その際、2つの加工を施します。

    1. パッケージ名を正規化する。pipはアンダースコア(_)とハイフン(-)を同一視しますが、uvはPEP 503準拠のcanonical nameを好みます。scikit_learn → scikit-learn、webdriver_manager → webdriver-manager。

    2. 開発専用パッケージを分離する。pytest等は本番の実行には不要なので、‍[dependency-groups].devに移します。

    完成形は以下のようになります。

    ‍▼keibaAI-v3/pyproject.toml

    [project]
    name = "keiba-ai-v3"
    version = "3.5.2"
    description = "Horse-racing prediction AI with LightGBM and automated IPAT voting via Playwright."
    readme = "README.md"
    requires-python = "==3.11.4"
    dependencies = [
        "apscheduler==3.11.0",
        "beautifulsoup4==4.12.3",
        "betacal==1.1.0",
        "lightgbm==4.5.0",
        "lxml==5.4.0",
        "matplotlib==3.8.2",
        "numpy==1.26.4",
        "pandas==2.2.2",
        "playwright==1.49.1",
        "python-dotenv==1.0.0",
        "PyYAML==6.0.1",
        "scikit-learn==1.4.0",
        "seaborn==0.13.2",
        "selenium==4.23.1",
        "tqdm==4.66.1",
        "webdriver-manager==4.0.2",
    ]
    
    [dependency-groups]
    dev = [
        "pytest==8.3.5",
        "ipykernel>=6.29",  # Jupyterカーネルとして認識させるため
    ]
    
    [tool.uv]
    default-groups = ["dev"]
    
    [tool.pytest.ini_options]
    testpaths = ["v3_5_2/tests"]

    各要素の意味を確認しておきましょう。

    • ‍requires-python = "==3.11.4"‍:厳密バージョン固定。「チーム全員同じ環境」が目的ならここは強く締めます。ゆるくて良ければ ‍">=3.11,<3.12"‍ のように範囲指定でもOKです。

    • dependencies:本番で必要なもの。requirements.txtから‍pytestを抜いて移しています。

    • ‍[dependency-groups].dev:開発時だけ必要なもの。uvはuv sync --no-devで除外できます。

    • ‍[tool.uv].default-groups = ["dev"]‍:デフォルトでdev群もインストールする設定。配布を受け取った人がすぐにテストやノートブックを回せます。

    • ‍[tool.pytest.ini_options].testpaths:pytestのデフォルト検索パス。複数バージョンのディレクトリがsys.path競合を起こす場合、現行バージョンのみに絞ります。

    ‍■ステップ4:uv lockでロックファイルを生成
    pyproject.tomlができたら、ロックファイルを生成します。

    uv lock

    uvがpyproject.tomlを解析して、直接依存 + 間接依存のすべてのバージョンを決定します。実行結果は以下のようになります。

    Using CPython 3.11.4 interpreter at: /path/to/pyenv/.pyenv/versions/3.11.4/bin/python3.11
    Resolved 139 packages in 568ms

    139パッケージというのは、「直接依存17個 + 間接依存122個」の合計です。そしてこの解決が568ミリ秒で完了する圧倒的速度が、uvの真骨頂と言えます(従来のpip-toolsだと数十秒〜数分かかっていた処理)。

    生成されたuv.lockの冒頭を覗いてみると、次のような構造になっています。

    ‍▼keibaAI-v3/uv.lock(冒頭の抜粋)‍

    version = 1
    revision = 3
    requires-python = "==3.13.7"
    resolution-markers = [
        "sys_platform == 'win32'",
        "sys_platform == 'emscripten'",
        "sys_platform != 'emscripten' and sys_platform != 'win32'",
    ]
    
    [[package]]
    name = "anyio"
    version = "4.13.0"
    source = { registry = "https://pypi.org/simple" }
    dependencies = [
        { name = "idna" },
    ]
    sdist = { url = "https://files.pythonhosted.org/packages/19/14/.../anyio-4.13.0.tar.gz",
              hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc",
              size = 231622 }
    wheels = [
        { url = "https://files.pythonhosted.org/packages/da/42/.../anyio-4.13.0-py3-none-any.whl",
          hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708",
          size = 114353 },
    ]

    1パッケージにつき‍[[package]]‍ブロックが1つ。そこに採用バージョン、間接依存、ダウンロード元URL、SHA256ハッシュ値がまとめて記録されています。

    ‍■ステップ5:uv syncで.venvを作る
    ロックファイルができたら、実際に.venv/を構築します。

    uv sync

    これで以下が全部行われます。

    1. プロジェクト直下に.venv/を作成

    2. .venv/内のPythonは.python-versionで指定された3.11.4

    3. uv.lockに記録された139パッケージをすべてインストール(dev含む)

    実行末尾は次のような出力になります。

     + webdriver-manager==4.0.2
     + webencodings==0.5.1
     + websocket-client==1.9.0
     + widgetsnbextension==4.0.15
     + wsproto==1.3.2

    確認コマンド。

    ls -la .venv/bin/python
    # lrwxr-xr-x  .venv/bin/python -> /Users/chiyoda/.pyenv/versions/3.11.4/bin/python3.11
    
    uv run python -c "import lightgbm, pandas, playwright, sklearn; print('OK')"
    # OK

    .venv/bin/pythonが3.11.4を指していればOKです。uvはPython本体をコピーせずシンボリックリンクで済ませるため、.venv/のサイズは従来のpython -m venvより小さく済みます。

    ‍■ステップ6:Playwright等の「ロックできない依存」を別途入れる
    uv.lockが管理できるのは、PyPI経由で配布されるPythonパッケージだけで、「別ダウンロードが必要なもの」は別途インストールする必要があります。

    具体的には、keibaAI-v3ではPlaywrightを使ってスクレイピングや自動投票を行っているので、下記のようなインストールを実行します。

    uv run playwright install

    ‍uv runを付けることで、.venv内のplaywright CLIが走ります。

    uv run playwright --version
    # Version 1.49.1

    Playwrightのバージョンとブラウザのバージョンは紐付いているので、‍playwright==1.49.1をpyproject.tomlで固定している限り、‍playwright installは常に対応するrevisionのブラウザを取得します。つまり実質的にはバージョンが固定されているわけです。

    4.2. バージョンをアップデートしたい場合

    上記はこのプロジェクトで元々使用していたPython 3.11.4 をそのまま移行する場合の手順でした。

    ‍■2026年4月時点のPythonバージョンの状況と推奨
    Pythonのバージョンについては、以下のような理由から定期的にバージョンを見直して新しいものに追従することが推奨されています。

    • セキュリティサポート:古いバージョンは時間が経つとサポートが終了し、以降は脆弱性が見つかっても修正パッチが出ないため、古いバージョンを使い続けるのはリスクになります

    • 実行速度の向上:Python 3.11以降は高速化プロジェクト(Faster CPython)の成果で、バージョンごとに速度改善が入っており、機械学習やデータ処理のような重い処理ほど、恩恵が大きくなります

    • 新しい言語機能:型ヒントの改善(‍PEP 695のtype文など)など、コードを短く安全に書ける機能が順次追加されています

    • ライブラリ側の要求:pandasやnumpy、scikit-learnなどの主要ライブラリは、新しい版で古いPythonのサポートを切っていきます。ライブラリを最新化するには、Python本体も追随しておく必要があります

    公式のリリーススケジュールをもとに、2026年4月時点のPythonバージョンを整理すると次の通りです。

    画像

    新しさを重視するなら3.14も十分選択肢ですが、一部提供が間に合っていないライブラリが存在し得るとの噂もあり、ここではPython 3.13系の最新パッチ(執筆時点で3.13.13)‍を選択することにします。

    ここからは、「uv管理のプロジェクトでPythonのバージョンをアップデートしたい場合」の手順について、このプロジェクトで実際にこの移行を行いながら解説していきます。uvを使っていると、この更新作業そのものが非常に軽くなります。

    まず目標バージョンが取得可能か確認します。

    uv python list --only-downloads --all-versions | grep 3.13

    出力(2026年4月時点):

    cpython-3.13.13-macos-aarch64-none                  <download available>
    cpython-3.13.13+freethreaded-macos-aarch64-none     <download available>
    cpython-3.13.12-macos-aarch64-none                  <download available>
    cpython-3.13.12+freethreaded-macos-aarch64-none     <download available>
    cpython-3.13.11-macos-aarch64-none                  <download available>
    cpython-3.13.11+freethreaded-macos-aarch64-none     <download available>
    cpython-3.13.10-macos-aarch64-none                  <download available>
    cpython-3.13.10+freethreaded-macos-aarch64-none     <download available>
    cpython-3.13.9-macos-aarch64-none                   <download available>
    cpython-3.13.9+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.8-macos-aarch64-none                   <download available>
    cpython-3.13.8+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.7-macos-aarch64-none                   <download available>
    cpython-3.13.7+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.6-macos-aarch64-none                   <download available>
    cpython-3.13.6+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.5-macos-aarch64-none                   <download available>
    cpython-3.13.5+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.4-macos-aarch64-none                   <download available>
    cpython-3.13.4+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.3-macos-aarch64-none                   <download available>
    cpython-3.13.3+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.2-macos-aarch64-none                   <download available>
    cpython-3.13.2+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.1-macos-aarch64-none                   <download available>
    cpython-3.13.1+freethreaded-macos-aarch64-none      <download available>
    cpython-3.13.0-macos-aarch64-none                   <download available>
    cpython-3.13.0+freethreaded-macos-aarch64-none      <download available>

    3.13.13が存在していることが確認できたので、早速このバージョンを反映させていきます。
    もし3.13.13が出てこない場合はuvのバージョンが古いので更新してください。
    まず最初に、.python-versionとpyproject.tomlの2ファイルをpython 3.13.13へ更新します。

    ▼keibaAI-v3/.python-version

    3.13.13

    ‍▼keibaAI-v3/pyproject.toml

    [project]
    name = "keiba-ai-v3"
    version = "3.5.2"
    description = "Horse-racing prediction AI with LightGBM and automated IPAT voting via Playwright."
    readme = "README.md"
    requires-python = "==3.13.13"

    併せてライブラリも最新化したいので、以下のようにバージョン指定を==から>=へ変えておきます。要は「現バージョンよりは新しくする」という制約のもと、各ライブラリ同士のバージョン要求が成立するような最新のライブラリが選ばれる、というわけです。

    ‍▼keibaAI-v3/pyproject.toml

    [project]
    name = "keiba-ai-v3"
    version = "3.5.2"
    description = "Horse-racing prediction AI with LightGBM and automated IPAT voting via Playwright."
    readme = "README.md"
    requires-python = "==3.13.13"
    dependencies = [
        "apscheduler>=3.11.0",
        "beautifulsoup4>=4.12.3",
        "betacal>=1.1.0",
        "lightgbm>=4.5.0",
        "lxml>=5.4.0",
        "matplotlib>=3.9.2",
        "numpy>=2.1.0",
        "pandas>=2.2.3",
        "playwright>=1.49.1",
        "pyarrow>=23.0.1",
        "python-dotenv>=1.0.0",
        "PyYAML>=6.0.1",
        "scikit-learn>=1.5.1",
        "seaborn>=0.13.2",
        "selenium>=4.23.1",
        "tqdm>=4.66.1",
        "webdriver-manager>=4.0.2",
    ]
    
    [dependency-groups]
    dev = [
        "pytest>=8.3.5",
        "ipykernel>=6.29",
        "jupyter>=1.0",
    ]
    
    [tool.uv]
    default-groups = ["dev"]
    
    [tool.pytest.ini_options]
    testpaths = ["v3_5_2/tests"]

    ‍これで準備が整ったので、後は以下のコマンドを実行すれば、一発で環境が入ります。

    uv sync --upgrade

    ‍uv sync --upgradeは、‍requires-python変更を反映したPythonのダウンロードと、全ライブラリの最新化を同時に行います。‍uv python install 3.13.13等は明示的に叩かなくても、‍uv syncが‍.python-versionを見て自動的に勝手にダウンロードしてくれます。

    無事同期が完了すると、以下を実行したときにPython 3.13.13が表示されます。

    uv run python -V         # Python 3.13.13 が表示されればOK

    最後に、playwrightだけは個別にインストールする必要があるので、以下を実行しておきます。

    uv run playwright install


    従来の‍pyenv install 3.13.13→ venv作り直し → ‍pip install -r requirements.txtの手順と比べると、圧倒的に手数が少なく、しかも数倍高速です。

    4.3. 従来コマンド↔uvコマンド比較

    このプロジェクトで、実際にどう変わったかを改めて比較してみます。

    ‍■Before:pyenv + venv + pip時代

    # 初回セットアップ(5〜10分)
    brew install pyenv
    pyenv install 3.11.4
    cd keibaAI-v3/
    pyenv local 3.11.4
    python3 -m venv .venv
    source .venv/bin/activate
    pip install -r requirements.txt
    
    # 日々の作業(毎回activateが必要)
    cd keibaAI-v3/
    source .venv/bin/activate
    python3 v3_5_2/src/05_execute_vote.py --kaisai_date 20260301
    
    # パッケージ追加(freeze忘れで事故)
    pip install new_package
    pip freeze > requirements.txt  # ← 忘れると共有時にバージョンがズレる

    ‍■After:uv時代

    # 初回セットアップ(30秒〜1分)
    curl -LsSf https://astral.sh/uv/install.sh | sh
    cd keibaAI-v3/
    uv sync
    uv run playwright install
    
    # 日々の作業(activate不要)
    cd keibaAI-v3/
    uv run python v3_5_2/src/05_execute_vote.py --kaisai_date 20260301
    
    # パッケージ追加(pyproject.toml と uv.lock が自動で揃う)
    uv add new_package

    見比べると、コマンドが半分以下になっていること、そしてactivate忘れやfreeze忘れの事故が構造的に起きないことが分かるかと思います。CIでのビルド時間も、pipベースだと数分かかっていたものがuv syncでは数十秒になります。

    最後に、その他のコマンドを含めた詳細な比較表を貼っておきます。

    画像

    ■補足①:Python実行方法について
    uv run
    を付けずに従来通りの感覚でpythonを実行すると、システムのPython(.venv/の外のもの)が使われてしまいます。

    # 間違い
    python v3_5_2/src/05_execute_vote.py --kaisai_date 20260301

    uvを導入したプロジェクトでは、‍.pyファイルを実行するときは常に uv run python で実行しましょう。

    # 正しい
    uv run python v3_5_2/src/05_execute_vote.py --kaisai_date 20260301

    また、Jupyter Notebook形式での実行の際は先ほど作成し‍た.venvを選びます。

    画像

    ■補足②:プロジェクトをまたいで使いたい場合
    black(フォーマッタ)やruff(リンター)のような、どのプロジェクトでも使いたいCLIツールは、各プロジェクトの依存には入れず、uvのグローバル領域に入れる方法もあります。

    uv tool install black        # グローバルにインストール
    black .                      # どこからでも呼べる

    ■補足③:一度だけ使いたい場合
    逆に一度だけ使いたい場合は、uvxコマンドを使うとインストールせずに一時実行することができます。

    uvx black .                  # インストールせずに一度だけ実行
    uvx ruff check .             # 同上

    5. ソースコードのダウンロード

    この移行については、完成済み競馬AIソースコードにも反映済みです。

    上記記事には、今回の記事で紹介したuv管理下のpyproject.toml・uv.lock含め、データ収集・機械学習モデル構築・サーバー上での自動投票など、本格的な競馬AIの運用に必要な全工程のコードが含まれています。ダウンロード後は、プロジェクトルートでuv syncを実行するだけで、本記事で解説した環境が丸ごと手元に再現されます。

    また、この【競馬AI開発】シリーズでは、#1から上記コードの作り方を詳しく解説しているので、ご自身で競馬予想AIを開発・活用してみたいとお考えの方は、ぜひ過去の記事も併せてご覧ください。

     
     
    競馬予想AIを作ることを通して、機械学習・データサイエンスの勉強になるコンテンツの発信や、筆者が行った実験の共有などを行っていきます。

    あなたへのおすすめ