ハクソク

世界を動かす技術を、日本語で。

Hackerたちの意見

著者が「pip vs poetry vs uvを気にしない」というポイントを言ってるけど、uvはこの使い方を直接サポートしてるんだよね。PyPIの依存関係も含めて、必要なのはuvと好みのPythonバージョンをインストールするだけなんだ。
俺もそう思ったけど、難しいのは非Pythonユーザーにとってはまだ明確じゃないってことだね。Clojureを使ったことがない人がClojureプロジェクトを始めると、ほぼ確実にLeiningenを使えってアドバイスが出てくる。Pythonの場合、ネットで検索するとuvを使えって言う人もいるけど、venvやpoetry、hatchを勧める人もいる。uvが主流になりつつあるとは思うけど、まだ普及してるわけじゃない。皮肉なことに、最近Goをインストールしたときも似たようなことがあった。Goを使ったことがなかったからaptでインストールしたら、バージョンが古すぎて間違ってたんだよね。でも、その場合は仮想環境で苦労するよりもずっと早く解決できたと思う。
実はもっと良い方法があるよ:#!/usr/bin/env -S uv run --python 3.14 --script これならPythonをインストールする必要もないんだ。uvが指定したPythonのバージョンをインストールしてコマンドを実行してくれる。
その依存関係って、グローバルになっちゃうんじゃない? それで衝突が起きる可能性もあるし。
俺もほとんどuvに移行したよ。必要なときは`uv pip`を使うけど、基本的には`uv add`に頼ってる。でも、`uv pip`を使い始めると、`uv pip`の欠点が出てくるんだよね。つまり、後から渡したものが前の依存関係の解決にも影響しちゃう。例えば、`uv pip install dep-a`の後に`... dep-b`をやるのと、`... dep-b`の後に`... dep-a`をやるのは全然違うし、`uv pip install dep-a dep-b`も同じじゃない。ちゃんと依存関係を解決して、ワークスペースを持つ環境から来ると、かなり混乱するよ。これはuvの問題というよりpipの問題なんだけど、俺の中では`uv pip`の方がまだマシだと思う。でも、Pythonのパッケージ管理は永遠に混乱したままだろうね。uvのバンドエイドでもこういう問題は解決できないみたい。
....でも、UVを使えるようにしないといけないし、いくつかのプラットフォーム(例えばラズベリーパイ)では、古いバージョンのRustのせいでビルドできないこともある。だから、Pythonで「pv」っていうスクリプトを書いたんだ。uvみたいにちょっとだけ動くやつ。笑っちゃったけど、どこでも動くし、俺の使い方には十分だよ。やったことは、原始的なAI生成のTOMLパーサーを埋め込むことだけだった。
この点については本当に分からないことが多い。まず、俺の中で「スクリプト」っていうのは、標準ライブラリ以外の依存関係を持たないか、持っても自分のシステムに特化したものだと思ってる。 (著者がこのニッチでGoの利点として挙げてるのは、依存関係なしでサクッとスクリプトが書ける標準ライブラリだよね! これってPythonの大きなセールスポイントじゃないの?)次に、依存関係があっても、これらのツールの違いを学ぶ必要はないんだ。好きなものを選んで使えばいい。三つ目、仮想環境っていうのは、依存関係をインストールするためのディスク上の場所で、設定ファイルと標準ライブラリが用意したワンライナーで自動的にセットアップされるスタブが含まれてるだけ。入って中を見たりする必要は全然ないし、アクティベーションスクリプトを使わなくても、venvの実行ファイルを指定すればいい。概念的には全然難しくないよ。四つ目、これらのサクッとしたスクリプトのために環境を共有するのは、実際にはほとんど問題なくうまくいくことが多い。ちゃんとした整理が当たり前になる前は何年もそれでやってたし、今でもそれで大丈夫なことが多い(ただ、今のプロジェクトのために隔離された環境を持つのが、依存関係を正しくリストアップする一番簡単な方法だってのはあるけど)。俺の経験では、サクッとしたスクリプトが互換性のないNumpyのバージョンに依存することはまずないよ。… それに、動的に提供する依存関係について考えたくないからって、コンパイル言語に切り替えるの?そんなにいいアイデアなら、そもそもPythonみたいな言語を作ろうなんて思わなかったはずだよ。で、えっと… >「受け取る側が最新のgoを持っていれば、そのスクリプトは将来何十年もどのOSでも動く。」って言ってるけど、いろんなシステムでPythonを動かそうとしたことがある人なら、その厄介な曲線がどれほど急か知ってるよね。ここでの擬似シェバンのトリックは、従来のものと同じようにWindowsではうまくいかないよ。そして、俺がWindowsからLinuxに切り替えたとき、Pythonの環境を整えるのは全然「急な厄介な曲線」じゃなかった。Linuxに慣れるのと同時にほぼ自動的にできたからね。(実際には意味のある`pyproject.toml`じゃなくて「.pyproject」って言ってるのは、ただの煽りだと思う。)
好きなPythonのバージョンすら必要ないよ、uvがそれをダウンロードしてくれるから。
俺は2019年にPyFlowでこれを解決したけど、誰も使わなかったから興味を失ったんだ。これはRustで書かれたOSSツールで、Pythonのバージョンやvenvsを自動的かつ透明に管理してくれる。`pyproject.toml`を設定して、`pyflow main.py`を実行すれば、あとはもう動くんだ。Cargoのように依存関係をインストールしてロックしてくれるし、プロジェクトに合ったPythonのバージョンをインストールして実行してくれる。あの時、PoetryやPipenvが人気のツールだったけど、十分じゃなかったんだ。依存関係の抽象化はうまくやってたけど、venvsやPythonのバージョンには対応してなかった。
これはマッド天才的な話だね。ただ…スクリプトを書くのは(俺の経験上)、出荷可能なソフトウェアとは違うエルゴノミクスが必要だと思う。うまく言えないけど、bashはすごくスクリプト向きで、goは出荷向き、pythonはその中間、rubyはbashに近い、rustはgoに近い感じ。良いスクリプトを書くには、使ってる構文の中で利用できるOSレベルの構造と、あまりツールが必要ない問題の組み合わせが大事なんだ。LSPやテストカバレッジとかはあんまり気にしないで、サクッと済ませたい仕事を片付けるための言語が求められてる。Goはそういう感じじゃない。Goで何かを作るときは、テストも一緒に持っていきたいし、ちゃんとしたビルドパイプラインを作りたいし、リリースプロセスも欲しい。こういう言語のエルゴノミクスについて考えたことはなかったから、他の人の意見も聞いてみたいな。
Pythonが「中間にいる」って話だけど、昨晩、バニラDebianで動かしたいシンプルなwebview gtkアプリのデモをやったんだ。で、今月の定番をやってuvを使ってvenvを作って依存関係を引っ張ってきたんだけど、コードを実行しようとしたら…めちゃくちゃだった。正しいものは揃ってるのにコードが動かないってエラーが出て、最終的にはPythonがコアダンプした…まあ、これが毎回Pythonを新しく試すときに起こることなんだ。Golangはもっと冗長だけど(mod.goシステムもあんまり好きじゃない)、一度コンパイルすればちゃんと動く。実行しようとしたり、OS特有のハックが必要になることはない。
> bash obviously is just using OS commands with syntactic sugar いや、bashは技術的にはPythonより「OS的」ってわけじゃないよ。単にbashが(よく)ターミナルエミュレーターのデフォルトシェルだからそうなってるだけ。
コードを書く時のエルゴノミクスが、LLMに編集を頼む簡単な方法があれば、あんまり問題にならないかも? それよりも、LLMが書いたコードの可読性を最適化する方が大事かもね。
初めの動機がよくわからないな。Pythonでスクリプトを書くのは好きなんだ。それが得意なことの一つだし、簡単なタスクをこなすためのスクリプトをすぐに書けるから、型やメモリについて心配する必要もない。だけど、大きなアプリケーションのメイン言語としてPythonを使うのは好きじゃない。
これはLinux特有みたいだね(他のUnix系OSでも動くのかな?)。Linuxにはスクリプト用に安定したシステムPythonがあるけど、これはGoをインストールする必要がある。シェルスクリプトやPython、他のスクリプト言語を使うこともできる。Pythonは後方互換性があまり得意じゃないけど、ほとんどのスクリプトにはほとんど問題がない。シェルスクリプトは後方互換性があるし、他の多くのスクリプト言語(例えばTCL)もそうだし、事前にインストールされている可能性が高い。Goをインストールするなら、uvをインストールしてPythonを使うこともできる。記事には「この投稿はほとんど釣りで始めた」って書いてあるけど、主な動機はGoに強い好みがあるからだと思う。
Pythonでスクリプトを書くのも大好きなんだけど、他の人のスクリプトをインストールするのは本当に嫌だな。
型については常に気をつけなきゃいけないよね。この関数は何を返すのか、何ができるのかを知っておく必要がある。言語をよく知っていれば、基本的な型については情報を探さなくても思い出せるけど、これは型付き言語でも同じことだよね。
せっかくだから、C++やアセンブリでも同じことをやったら? スクリプトが嫌いすぎて、コンパイル言語を使うために大変な思いをして、スクリプト言語の利点を全部捨てるなんて、言語が嫌いだからって技術的な理由じゃないよね。おめでとう、貴重な時間を無駄にしたね。 > 「便利さの代償はスケールの難しさ」 もちろん、スケールなんてできないよ。スケールを考え始めたら、捨てるつもりで書いたスクリプトじゃなくて、ちゃんとしたものを作るべきだよ。それはPythonやbashを完全に排除する理由にはならないけどね。PythonコードをGoに変換するコストは、今やほぼゼロだから、必要があればやればいい。早すぎる最適化については、もう十分言われてるし。 > 「異なるシステムでPythonを動かそうとしたことがある人なら、どれだけ面倒な曲線か知ってるよね。」 もし特定のバージョンのライブラリが10個必要なら、もうそれはスクリプトとは呼べないよね。ちゃんとしたパッケージ管理が必要なプロジェクトになっちゃう。Goと同じように。
PythonとC++の間には、PythonとGoの間よりもずっと大きなエルゴノミクスのギャップがあるよ。コンパイル時間やパッケージ管理がC++の大きな欠点だし。「コンパクトカーよりSUVの方がいい? だったらバイクに乗った方がいいじゃん!」
俺もこれ作ったよ!//は使わないことにしたんだ。エディタでgofmtの自動フォーマットを使ってるから、//とusrの間にスペースが入っちゃうんだよね。このやつはgofmtで変わらないやつだよ:/*?sr/bin/env go run "$0" "$@"; exit $? #*/
Goはシェバンのサポートを追加することを明確に拒否していて、このハックを強制してるんだ。リソースの「乱用」と見なされてるからね。[0] その代わりに、Pikeが間違いだとも呼んでいる`gorun`を推奨してる。これを使えばパスをハードコーディングしなくても済むようにこの方法を変更できる。/// 2>/dev/null ; gorun "$0" "$@" ; exit $? >良い古きPOSIXマジックだね。LLMに聞くと、シェバンのせいだって言うだろうね。まあ、ChatGPTは記事と同じ説明をしてるから、何度も繰り返されてきたメカニズムを考えれば驚くことじゃないけど。 >Go、Nim、Zig、Dはすべて`-run`引数を持っていて、似たように使える。他にもSwift、OCaml、Haskellはファイルを直接実行できて、引数を提供する必要がない。[0]: https://groups.google.com/d/msg/golang-nuts/iGHWoUQFHjg/_dbL...
> 一つ大きな問題がある:gopls。スクリプトの最初の行はスペースなしである必要がある…具体的には自動フォーマットの問題だ。goplsは通常、編集中に保存するときにこれを行うけど、CIシステムでマージされた*.goファイルが正規のフォーマットであることを強制するのは良いプラクティスだよ。これによって、変更を加えたユーザーがその行をフォーマットすることになり(その行について責任を負うことになる)、他の誰かがそのファイルの別の部分に触れることによる不運を避けられる。これにより、マージの競合も減るんだ。でも、このアプローチにはもう一つ大きな(もっと大きな)問題がある:一回限りのスクリプトでgo.modファイルを使えないから、依存関係のバージョンを指定できない。これが、あなたの投稿を動機づけた互換性の魅力を損なうんだ:> goスクリプトの主な利点は[...] 互換性の保証だ。ほとんどの言語は後方互換性を目指しているけど、goはそれをコア機能にしている。「goスクリプト」を書いても、goのバージョン1.*を使っている限り動かなくなることはないから、企業環境にはぴったりだよ。 > さらに、互換性の保証があれば「スクリプト」を共有するのもずっと簡単になる。受け取る側が最新のgoを持っていれば、そのスクリプトは将来何十年もどのOSでも動く。
確かに、メジャーバージョンはインポートパスを通じて固定されていて、互換性があるはずだよ。
同じように、最近すべてのビルドとデプロイスクリプトをGoに変えたんだ。実際の利点は、サービスで使うユーティリティ関数をデプロイで共有できることだった。だから、サービスやDBがオンラインかどうかを確認したり、クラウドのシークレットに均一な方法でアクセスするコードを簡単に共有できた。エラーチェックもずっと明確になったし(curlが失敗したのはオフラインなのか、形式が間違っているのかが分かる)。さらに、goモジュールを使うことでさらに強力になる。すべてのスクリプトが共有の「scripts」モジュール内の単一の関数を呼び出すようにすれば、どこからでも対称的に呼び出せるようになる。これにより、すべてのスクリプトがビルドされることが保証されるし、常に実行されているわけではなくても大丈夫だ。どのスクリプトもscripts.DeployService(…)を呼び出せるし、どのディレクトリにいても、誰が呼び出しても気にしない。引数がそれぞれのスクリプトに必要なパスや設定を明確にしてくれるんだ。
これがPythonについての長い愚痴になると思ってたけど、実際には「消えてほしい」って感じ。長いことPythonを使ってきたプログラマーで、かつてはこの言語の熱心な支持者だったから、その意見も考慮に入れるけど。MLが全部Pythonでやってるのは、正直言って大きな間違いだと思う。これから何年もそのツケを払うことになるよ。主な理由は、遅いこと、型システムが他の言語よりもずっと使いにくいこと、そして配布が難しいこと。使う理由は inertia(慣性)だけ。慣性は多くの理由で十分かもしれないけど、業界にはPythonを最後に考えてもらって、代わりにTypeScript、Go、Rust(用途によるけど)をベストプラクティスとして考えてほしい。Pythonは非推奨とされて、既存のコードベース(例えばPyTorch)でだけ使われるべきだと思う。なんでウェブアプリをPythonで書くの?型はひどいし、遅いし。もっといい代替手段があるよ。
TypeScript?なんでPythonみたいな素敵な言語をJavaScriptの何かに置き換えなきゃいけないの?
Pythonをシンプルで表現力豊かで強く型付けされた、ネイティブコードにコンパイルできる何かに置き換えたいな。内部APIを使うための便利なCLIツールをちょこちょこ作る習慣があるんだけど、そういうのに関してGoとPythonのパフォーマンスの違いがわかるなんて思わないかもしれないけど、実際にはわかるよ。Goでこれらのツールを書いて1年くらい経った後、LOC(行数)の違いがはっきりしてるからPythonに戻ったけど、毎回それを実行するたびにGoで書かれてたらいいのにって思う。(OCamlが多分求めてるものだけど、20世紀のアカデミックな言語のツールや依存関係管理に取り組むのが面倒で、モチベーションが上がらないんだよね。)
>「仮想環境を持ちたくないし、pip、poetry、uvの違いを学びたくない。どうでもいい。ただコードを実行したいだけ。」っていうのはスキルの問題だね。このブログ記事は。`uv run`とPEP 723が、著者が説明しているすべての問題を解決したよ。