Pythonの.pycファイルって何のためにある? Pythonの「コンパイル」とキャッシュの仕組み
Pythonでプログラミングを始めたばかりの頃、ふと自分のフォルダに見慣れないものが増えていることに気づきませんか?
それは pycache という名前のフォルダや、その中にある .pyc という拡張子のファイルです。
自分の書いたコードではないファイルが自動的に作られると、ウイルスかな?消しても大丈夫かな?と不安になることもあるでしょう。 しかし安心してください、これはPythonがあなたのプログラムをより速く動かすために用意してくれたご褒美のようなものです。
今回は、エンジニア歴10年の視点から、この不思議なファイルの正体とPythonの裏側で起きている仕組みを徹底的に解説します。
謎のファイル「.pyc」の正体とは?¶
結論から言うと、.pycファイルとは Pythonのソースコードをバイトコードという形に変換したもの です。 私たちが書くPythonのコードは人間には読みやすいですが、実はコンピューターがそのまま理解するには少し複雑すぎます。
コンピューターはもっと単純な命令の羅列を好むため、Pythonは実行の直前に自分専用の言葉に翻訳作業を行います。 この翻訳されたデータが保存されている場所こそが、あの謎のファイルたちの正体なのです。
そもそもなぜ自動で作られるの?¶
Pythonはプログラムを実行するたびに、ソースコードを読み込んで翻訳する手間をかけています。 もし1000行あるプログラムを何度も動かす場合、毎回1から翻訳し直すのは少し時間がもったいないと思いませんか。
そこでPythonは、一度翻訳した結果を キャッシュ(一時保存) として残しておく仕組みを持っています。 次回から「あ、前にも同じコードを翻訳したな」と気づくと、翻訳済みの.pycファイルを直接読み込むことで起動時間を短縮します。
「pycache」フォルダの役割¶
以前のPythonでは、.pycファイルはソースコードと同じ場所にバラバラと作られていました。 これではプロジェクトのディレクトリが汚れてしまい、管理が非常に面倒だった記憶があります。
現在のPython 3では、これらを1箇所にまとめるために pycache という専用のフォルダが自動生成されるようになりました。 おかげで、私たちの作業環境を綺麗に保ちながら、裏側でこっそり高速化の恩恵を受けられるようになっています。
【関連記事】 Pythonの仮想環境(venv)って何のためにある?プロジェクトごとに混ぜない管理法
Pythonの実行プロセスを深掘りする¶
Pythonはよく インタプリタ言語 と呼ばれますが、実は裏側で コンパイル という作業を行っています。 このプロセスの違いを理解することが、中級エンジニアへの第一歩となります。
一般的なプログラムが動くまでの流れは、大きく分けて以下の4つのステップに分かれています。 少し専門的な言葉も出てきますが、イメージを掴むだけで十分ですので読み進めてみてください。
ステップ1:ソースコードの読み込み¶
まずは、私たちが書いた .pyファイル の中身をPythonが1行ずつ読み取っていきます。 この段階ではまだただのテキストデータであり、コンピューターは何をすべきか分かっていません。
ステップ2:構文解析(パース)¶
次に、読み取ったコードが文法的に正しいかどうかをチェックする 構文解析 が行われます。 カッコの閉じ忘れやインデントのミスでエラーが出るのは、このステップでPythonが「意味がわからないよ」と困っているからです。
ステップ3:バイトコードへのコンパイル¶
文法に問題がなければ、ここでついに バイトコード への変換が行われます。 バイトコードは、人間には解読不能なバイナリデータですが、Pythonの仮想マシンにとっては非常に読みやすい形式です。
この翻訳済みのデータが、今回の主役である .pycファイル として保存されることになります。 一度この形にしてしまえば、次からはステップ1と2をスキップして、いきなりステップ4へ進めるわけです。
ステップ4:PVM(Python仮想マシン)での実行¶
最後に、PVM(Python Virtual Machine) と呼ばれる実行エンジンがバイトコードを処理します。 PVMは、バイトコードという命令を受け取って、実際のコンピューターのCPUが動ける形に橋渡しをしてくれます。
.pycファイルがある時とない時の違い¶
.pycファイルがあることで、具体的にどれくらいのメリットがあるのか気になるところですよね。 ここでは、実行速度や動作の挙動について詳しく見ていきましょう。
実は、.pycファイルがあっても 「計算速度そのもの」は速くなりません。 速くなるのは、あくまでプログラムが動き出すまでの 「起動時間」 です。
以下の表に、それぞれの状態での違いをまとめました。
| 項目 | .pycがない場合 | .pycがある場合 |
|---|---|---|
| 起動時間 | 翻訳(コンパイル)が必要なため、少し時間がかかる | 翻訳済みファイルを読み込むため、非常に速い |
| 実行速度 | 結局同じPVMで動くため、差はない | 結局同じPVMで動くため、差はない |
| ファイル構成 | ソースコード(.py)のみでシンプル | キャッシュフォルダが作成される |
| 自動更新 | 常に最新のコードが読み込まれる | コード変更を検知して自動で再生成される |
開発者が意識すべき更新の検知¶
Pythonは賢いので、あなたがソースコード(.py)を書き換えたことをちゃんと知っています。 ソースコードの更新日時と.pycファイルの記録を比較して、古くなっていれば自動的に新しく作り直してくれます。
そのため、私たちが手動で.pycファイルを更新したり管理したりする必要は一切ありません。 たまに古いキャッシュのせいでバグが起きるのでは?と心配する方がいますが、現代のPythonではまず起こり得ません。
.pycファイルを消してしまったら?¶
もし間違えて pycache フォルダをゴミ箱に入れてしまっても、全く問題ありません。 次にプログラムを実行した瞬間に、Pythonが「あ、ないから作り直そう」と自動で再生成してくれるからです。
Gitなどのバージョン管理ツールを使っている場合は、これらのキャッシュファイルは共有する必要がないため、無視するのが一般的です。 むしろ、他人の環境で作られた.pycファイルを自分の環境で使うとトラブルの元になることもあります。
【関連記事】 Pythonはなぜ“インタプリタ言語”なのか?インタプリタ言語とは何なのか解説!
【実践】バイトコードの中身を覗いてみよう¶
理論だけではつまらないので、実際にPythonがどんな「バイトコード」に変換しているのか見てみましょう。 標準ライブラリの dis モジュールを使えば、誰でも簡単に中身を確認できます。
例えば、以下のような非常にシンプルな足し算をする関数があるとします。 このコードがPythonの内部でどう解釈されているのかを暴いてみましょう。
def add_numbers(a, b):
return a + b
import dis
dis.dis(add_numbers)
このコードを実行すると、コンソールには以下のような出力が表示されるはずです。 普段見慣れているPythonとは全く違う、アセンブリ言語のような結果が出てきます。
2 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_ADD
6 RETURN_VALUE
出力結果の読み解き方¶
出力された内容を上から順番に見ていくと、Pythonの思考プロセスが分かります。
まず LOAD_FAST で変数 a と b を準備し、BINARY_ADD でそれらをガッチャンコして足し合わせています。
最後に RETURN_VALUE で結果を返して終了、という非常にシステマチックな流れです。
Pythonは、私たちが書いた1行の足し算を、このように細かなステップに分解して.pycファイルに保存しているのです。
エンジニア歴10年の私が経験した「.pyc」の裏話¶
ここからは、私が現場で実際に体験した .pyc にまつわるエピソードをお話しします。 初心者の方が同じ罠にハマらないための、ちょっとした知恵袋として活用してください。
昔々のPython 2が全盛期だった頃は、キャッシュの仕組みが今ほど洗練されていませんでした。 当時は pycache フォルダがなく、.pyc が .py と同じ場所に生成されていました。
エピソード1:消したはずのコードが動く怪現象¶
あるプロジェクトで、不要になったスクリプト(.py)を削除したことがありました。 しかし、なぜかそのプログラムをインポートしようとすると、エラーにならずに動いてしまったのです。
原因は、ソースコードを消したのに .pycファイルだけが残っていたこと でした。 Pythonは .py がないなら、隣にある .pyc を読み込めばいいか、と判断してしまい古いロジックを動かし続けていたのです。
このゾンビコード現象には数時間頭を抱えましたが、現在のPython 3ではこの挙動は改善されています。 それでも、何か挙動が怪しい時はキャッシュを疑うという癖は、今でもデバッグの役に立っています。
エピソード2:環境移行時のコンパイルエラー¶
別のエンジニアから送られてきたファイルを、自分のPCで動かそうとした時のことです。 なぜか起動時に不気味なエラーが出て、全く動かなくなってしまいました。
実は、.pycファイルには Pythonのバージョン情報 が埋め込まれています。 Python 3.8で作られた .pyc ファイルを、Python 3.12で無理やり動かそうとするとエラーになることがあります。
初心者のうちは、他人にコードを渡す時に pycache ごと送ってしまうことがよくあります。 しかし、それは相手の環境を汚す原因になるので、ソースコードの .py だけを渡すのが作法だと学びました。
コンパイル言語とPythonの違いを比較¶
ここで、C言語やJavaといった他の言語とPythonの「コンパイル」がどう違うのかを整理しておきましょう。 Pythonの立ち位置がわかると、なぜこれほどまでに開発が楽なのかが理解できます。
完全なコンパイル言語(C言語など)¶
C言語などは、プログラムを実行する前に 機械語 というCPUが直接理解できる形式に完全に変換します。 この作業には時間がかかりますが、一度コンパイルしてしまえば実行速度は世界最速クラスです。
ただし、Windowsでコンパイルした実行ファイルはMacでは動かないなど、移植性が低いという弱点があります。 また、1文字直すたびに数分かけてコンパイルし直す必要があるため、開発のテンポは少し遅くなりがちです。
ハイブリッドな中間言語(Javaなど)¶
JavaはPythonと似ていて、一度中間形式(クラスファイル)にコンパイルしてから、専用の仮想マシンで動かします。 Pythonとの最大の違いは、Javaはこのコンパイル作業を 開発者が明示的に行う必要がある という点です。
Pythonはこれを勝手に、気づかないうちに、やってくれるため、Javaよりもさらに手軽に扱えます。 この手軽さと、そこそこの速度のバランスこそが、Pythonが世界中で愛される理由の1つです。
言語タイプ別・特徴一覧¶
| 言語 | 形式 | 変換のタイミング | 速度 | 開発のしやすさ |
|---|---|---|---|---|
| C / C++ | コンパイル型 | 実行前(手動) | 爆速 | 低い |
| Java | 中間言語型 | 実行前(手動) | 速い | 普通 |
| Python | スクリプト型 | 実行時(自動) | 普通 | 非常に高い |
| JavaScript | スクリプト型 | 実行時(自動) | 速い | 高い |
【関連記事】 Pythonの歴史をやさしく解説!誕生秘話からAI時代まで
Pythonのキャッシュ機能を無効にする方法¶
基本的には放置して良い .pyc ファイルですが、特定のケースでは生成を止めたくなることもあります。 例えば、読み取り専用のファイルシステムで動かしている時や、絶対に環境を汚したくない時などです。
そのような場合は、環境変数を設定することで簡単にキャッシュ生成をオフにできます。 コマンドラインや設定ファイルで PYTHONDONTWRITEBYTECODE=1 と記述するだけです。
export PYTHONDONTWRITEBYTECODE=1
python my_script.py
こうすることで、Pythonは pycache を作らず、常にメモリ上だけで翻訳を行うようになります。 ただし、起動するたびに翻訳作業が発生するため、大規模なライブラリを使っている場合は起動が目に見えて遅くなるでしょう。
特別な理由がない限りは、Pythonのデフォルトの挙動に任せておくのがベストな選択です。 エンジニアの世界には推測するな、計測せよ、という言葉がありますが、キャッシュの効果は計測してみると意外とバカにできません。
なぜ .pyc は Git 管理してはいけないのか?¶
実務でチーム開発を始めると、必ず .gitignore というファイルに出会うはずです。
そこには必ずと言っていいほど __pycache__/ という記述が含まれています。
なぜ、せっかく高速化してくれるファイルを共有してはいけないのでしょうか。 その理由は、.pyc ファイルが 実行環境に依存する からです。
理由1:OSやバージョンの違い¶
先ほども触れた通り、.pyc は特定の Python バージョン向けに最適化されています。 あなたが Mac の Python 3.10 で生成したファイルを、Windows の Python 3.12 を使う同僚に渡しても動かない可能性が高いのです。
そんな動かない可能性のあるファイルを Git にアップロードしても、リポジトリの容量を無駄に食うだけになってしまいます。 ソースコードさえあれば誰の環境でも .pyc は再生成できるので、共有する必要は全くありません。
理由2:無駄な差分が発生する¶
.pyc ファイルはバイナリデータなので、ソースコードを1文字変えるだけで中身がガラッと変わります。 これを Git で管理してしまうと、コードを修正するたびに .pyc の巨大な差分が記録されてしまいます。
これでは、本当に大切なソースコードの変更履歴が埋もれてしまい、レビューが困難になります。 美しいリポジトリを保つためにも、キャッシュファイルは自分の手元にだけ置いておくのが良いでしょう。
Pythonの内部構造を知るメリット¶
ここまで読んでくださったあなたは、もう .pyc ファイルを理解しているはずです。 最後に、なぜこうした内部構造を知ることが大切なのかをまとめます。
プログラミングを始めたばかりの頃は、動けばいい、という感覚で十分です。 しかし、中級者へとステップアップするためには、自分のコードがどう処理されているかを知る必要があります。
トラブルシューティング能力の向上¶
例えば、環境を変えた時にだけ動かない、という問題に直面したとします。 もしキャッシュの仕組みを知っていれば、まず pycache を消してみる、という選択肢が浮かびます。
こうした引き出しの多さが、熟練エンジニアと初心者の決定的な差になります。 仕組みを理解していれば、エラーメッセージを読んだ時の理解度も格段に変わってくるはずです。
パフォーマンスへの意識¶
Pythonが裏側で翻訳作業をしていると知れば、インポートの回数やライブラリの量にも意識が向くようになります。 無駄に重いモジュールを何度も読み込むことが、どれだけ起動時間に影響を与えるか想像できるようになるからです。
こうした小さな意識の積み重ねが、最終的にはユーザーにとって使い心地の良いプログラムへと繋がります。 知識は武器です。今日学んだ .pyc の知識も、いつか必ずあなたの役に立つ時が来ます。
まとめ¶
今回は、Pythonの裏側で働く .pyc ファイルとコンパイルの仕組みについて解説しました。 一見すると邪魔に見える pycache フォルダも、実は私たちの開発効率を支える大切な仕組みです。
Pythonが自動で翻訳と保存を繰り返してくれるおかげで、私たちは複雑な内部構造を気にせず、コードを書くことだけに集中できます。
今回の記事のポイントです。
- .pycファイル は、Pythonのコードを読みやすく翻訳したバイトコードの塊。
- pycache フォルダに保存され、2回目以降の起動を速くしてくれる。
- ソースコードを書き換えると、Pythonが勝手に新しく作り直してくれるので放置でOK。
- Git管理からは除外するのがエンジニアの常識。
- もし消してしまっても、次に実行する時に自動で復活するので心配無用。
初心者のうちは、どうしても目に見えるコードのことばかりを考えてしまいがちです。 しかし、今回のように実行の裏側に目を向けることで、より効率的でトラブルに強いプログラムが書けるようになります。
これからも、Pythonという頼もしい相棒の仕組みを1つずつ紐解きながら、プログラミングを楽しんでいきましょう!
ここまでお読みいただきありがとうございました。