Python WebAcademy Blog

Pythonのsysモジュールとは?argv・path・exitなどよく使う機能を初心者向けに解説

|

import sysはよく見かけるのに、何ができるモジュールなのかはっきりしない。そんな方に向けて、Python標準ライブラリのsysモジュールでよく使う機能を整理しました。コマンドライン引数のsys.argv、モジュールの探し場所を決めるsys.path、終了コードを返すsys.exit、エラー出力のsys.stderrまで、手元で動かした結果とサンプルコードで解説します。

ネットで見つけたPythonのコードをのぞくと、先頭に import sys と書かれていることがよくあります。でも、このsysが何をするモジュールなのか、説明できる人は意外と少ないのではないでしょうか。

名前からして難しそうですし、初心者のうちは見なかったことにしがちですよね。私も学び始めたころは、おまじないのように書き写していました。

sysは、いま動いているPythonそのものとやり取りするための標準ライブラリです。プログラムに渡された引数を受け取ったり、終了のしかたを決めたり、Pythonのバージョンを調べたりできます。

今回は、sysの中でも特によく使う機能に絞って、手元のPython 3.11で動かした結果とともに紹介します。

sysモジュールとは何か

まずは、sysがどんな立ち位置のモジュールなのかを押さえておきましょう。sysはsystemの略で、Pythonの実行環境に関する情報や操作をまとめたモジュールです。

ここでいうシステムは、パソコン全体ではありません。いま動いているPythonインタープリタ自身のことを指しています。インタープリタとは、書いたコードを1行ずつ読み取って実行してくれるプログラムのことです。

標準ライブラリなので、pipでのインストールは不要です。Pythonが入っていれば、import sys と書くだけで使えます。

よく似た名前にosモジュールがあります。ざっくり言うと、osはパソコンのOSとやり取りする道具で、sysはPython自身とやり取りする道具です。ファイルやフォルダを操作したいならos、Pythonの設定や状態を知りたいならsys、と覚えておくと迷いにくくなります。

よく使う機能を一覧で見てみよう

sysにはたくさんの機能がありますが、初心者が実際に使うものは限られています。最初に、この記事で扱うものを表で整理しておきます。

名前 種類 できること
sys.argv リスト コマンドライン引数を受け取る
sys.exit() 関数 終了コードを指定してプログラムを終える
sys.path リスト モジュールを探す場所を確認する
sys.version_info タプル Pythonのバージョンを調べる
sys.platform 文字列 動いているOSの種類を調べる
sys.stdin / stdout / stderr ファイル 入力・出力・エラー出力を扱う
sys.getsizeof() 関数 オブジェクトのメモリ上のサイズを調べる
sys.getrecursionlimit() 関数 再帰呼び出しの上限を確認する

sys.argvでコマンドライン引数を受け取る

sysの中でいちばん出番が多いのが、sys.argvです。argvはargument vectorの略で、プログラムを起動したときに渡された引数が入っています。

たとえば、ターミナルで python greet.py hello 3 のように実行したとしましょう。このとき、スクリプト名のうしろに書いたhelloと3がコマンドライン引数です。

中身を確かめるために、次の2行だけのファイルを用意しました。

import sys
print(sys.argv)

これをa.pyという名前で保存し、python a.py hello 3 と実行すると次のように表示されました。

['a.py', 'hello', '3']

先頭の要素には、実行したスクリプトの名前が入ります。そのうしろに、渡した引数が順番に並んでいる形です。

引数はすべて文字列になる

ここで注意したいのが、3という数字も文字列の '3' として入っている点です。sys.argvの要素は、どれも文字列として渡されます。

数として計算に使いたいときは、int(sys.argv[2]) のように自分で変換する必要があります。これを忘れて足し算をすると、3+3が33になるような不思議な結果になるので気をつけてください。

引数を渡し忘れたときに、sys.argv[1] を参照してIndexErrorになるのも定番のつまずきです。先に len(sys.argv) で個数を確認しておくと安心です。

引数の数が増えてきたら、標準ライブラリのargparseに切り替えるのがおすすめです。ヘルプの表示や型の変換まで面倒を見てくれます。【関連記事】Pythonでコマンドラインツールを作るには?argparse入門

sys.exitでプログラムを終わらせる

次は、プログラムを途中で終わらせるsys.exitです。単に止めるだけなら、ほかにもexitやquitがありますよね。

exitやquitは、対話モードで使うために用意された関数です。スクリプトの中で終了させたいときは、sys.exitを使うのが正しいお作法とされています。この違いは、こちらの記事でくわしく解説しています。【関連記事】Pythonの抜け方は?exit関数の使い方を解説

sys.exitの引数には、終了コードを渡せます。終了コードとは、プログラムが成功したのか失敗したのかを、呼び出し元に伝えるための数字です。

渡す値によって、動きが次のように変わります。

引数 終了コード 動き
省略、またはNone 0 成功として終わる
整数 その数 0なら成功、0以外なら失敗
文字列など整数以外 1 その内容をエラー出力に表示して失敗として終わる

文字列を渡したときの動きを確かめてみました。メッセージが表示されたあと、終了コードを表す $? は1になっています。

エラー: ファイルがありません
code=1

慣習として、Unix系のプログラムでは、コマンドラインの使い方の誤りに2、それ以外のエラーに1を使うことが多いと公式ドキュメントでも紹介されています。最初は、成功なら0、失敗なら0以外と覚えておけば大丈夫です。

sys.exitの正体は例外

意外に思うかもしれませんが、sys.exitは内部でSystemExitという例外を投げているだけです。そのため、try文のfinallyに書いた後片づけの処理は、きちんと実行されます。

裏を返すと、例外をまとめて受け止めるexceptを書いていると、終了そのものを握りつぶしてしまうことがあります。実際に、SystemExitをexceptで受け止めてみると、次のように捕まえられました。

import sys

try:
    sys.exit(3)
except SystemExit as e:
    print("caught", e.code)

実行すると caught 3 と表示され、プログラムは終わらずに先へ進みます。終了したつもりなのに処理が続いて困ったときは、どこかで例外を広く受け止めていないか疑ってみてください。

sys.pathでモジュールの探し場所を知る

import を書いたとき、Pythonはどこからそのモジュールを探しているのでしょうか。その答えが入っているのが、sys.pathです。

sys.pathは、モジュールを探すフォルダのパスを並べたリストです。Pythonは先頭から順番にフォルダをのぞき、最初に見つかったモジュールを読み込みます。

先頭の要素には、基本的に実行したスクリプトが置かれているフォルダが入ります。先ほどのa.pyで print(sys.path[0]) を実行すると、ファイルのあるフォルダが表示されました。

/root/work

ModuleNotFoundErrorに悩んだときは、sys.pathを表示してみるのが近道です。インストールしたはずの場所がリストに入っていなければ、別のPythonで実行している可能性が高いと見当がつきます。【関連記事】PythonのModuleNotFoundErrorって何?解決するにはどうしたらいいの?

自分のファイル名に注意

sys.pathの先頭がスクリプトのフォルダだということは、同じフォルダのファイルが優先されるということです。ここに、初心者がはまりやすい落とし穴があります。

たとえば、練習用のファイルをrandom.pyという名前で保存したとします。すると、標準ライブラリのrandomではなく、自分のrandom.pyが読み込まれてしまうことがあります。

sys.version_infoとsys.platformで実行環境を調べる

ここからは、実行環境を調べるための機能を見ていきます。チームで同じコードを動かすとき、相手のPythonが古くて動かないという経験はありませんか。

sys.version_infoには、Pythonのバージョンが名前付きタプルとして入っています。手元の環境では次のように表示されました。

sys.version_info(major=3, minor=11, micro=17, releaselevel='final', serial=0)

便利なのは、タプルどうしで大小を比べられることです。sys.version_info >= (3, 10) と書けば、3.10以上かどうかをTrueかFalseで判定できます。

sys.platformには、動いているOSの種類が入っています。Linuxならlinux、Windowsならwin32、macOSならdarwinという文字列です。OSによって処理を分けたいときの目印になります。

標準入出力とエラー出力を使い分ける

続いて、sys.stdin、sys.stdout、sys.stderrの3つです。それぞれ標準入力、標準出力、標準エラー出力と呼ばれる、データの通り道を表しています。

ふだん使っているprint関数は、標準出力に文字を書き出しています。input関数は、標準入力から1行を読み取っています。

では、標準エラー出力は何のためにあるのでしょうか。答えは、結果とエラーメッセージを分けて扱えるようにするためです。

python script.py > out.txt のように実行すると、標準出力の内容はファイルに保存されます。このとき、エラーメッセージまでファイルに混ざってしまうと困りますよね。

print("メッセージ", file=sys.stderr) と書けば、エラー出力に送れます。結果はファイルへ、エラーは画面へと分かれるので、あとから結果を扱う人にもやさしい作りになります。

sys.getsizeofと再帰の上限

ここでは、困ったときに役立つ機能を2つ紹介します。

sys.getsizeofは、オブジェクトがメモリ上で何バイト使っているかを返す関数です。手元で空のリストを調べると56、要素が3つのリストでは88と表示されました。

ただし、この数字には落とし穴があります。1000個の要素を持つリストを1つだけ入れたリストを調べると、64しかありませんでした。

これは、getsizeofがそのオブジェクト自身の大きさだけを数えるからです。中から参照している別のオブジェクトの大きさは含まれません。データ全体のメモリ使用量を知りたいときは、この点に注意してください。

もうひとつは、sys.getrecursionlimitです。関数が自分自身を呼び出す再帰には深さの上限があり、手元の環境では1000でした。

上限を超えるとRecursionErrorになります。sys.setrecursionlimitで上限を上げることもできますが、むやみに上げるとPython自体が落ちる危険があります。まずは再帰を使わない書き方を考えるのが先です。【関連記事】Pythonの再帰関数とは?自分を呼び出す関数の考え方とRecursionErrorの直し方を初心者向けに解説

sysを使った小さなスクリプトを作ってみる

ここまでの内容を組み合わせて、ファイルの行数を数える小さなスクリプトを作ってみましょう。次のコードを、count_lines.pyという名前で保存します。

import sys

if sys.version_info < (3, 8):
    sys.exit("Python 3.8以上で実行してください")


def main():
    if len(sys.argv) < 2:
        print("使い方: python count_lines.py ファイル名", file=sys.stderr)
        return 2

    path = sys.argv[1]
    try:
        with open(path, encoding="utf-8") as f:
            count = sum(1 for _ in f)
    except FileNotFoundError:
        print(f"ファイルが見つかりません: {path}", file=sys.stderr)
        return 1

    print(f"{path}: {count}行")
    return 0


if __name__ == "__main__":
    sys.exit(main())

最初にバージョンを確認し、古ければメッセージを出して終わります。main関数では、引数の有無を確かめてから、ファイルを開いて行数を数えています。

ポイントは、main関数が終了コードを返し、それをsys.exitに渡している点です。この形にしておくと、成功と失敗を呼び出し元へきちんと伝えられます。

if __name__ == "__main__": の意味が気になった方は、こちらを読んでみてください。【関連記事】Pythonの「name == 'main'」って何?初心者が必ず疑問に思うこと

3行あるmemo.txtを用意して、3通りの呼び方を試した結果が次のとおりです。

使い方: python count_lines.py ファイル名
code=2
memo.txt: 3行
code=0
ファイルが見つかりません: nai.txt
code=1

引数なしでは2、成功すれば0、ファイルがなければ1が返っています。エラーメッセージはstderrに出しているので、出力をファイルに保存してもメッセージは混ざりません。

現場で感じた、終了コードの大切さ

私は10年ほどエンジニアとして開発に関わってきましたが、終了コードを軽く見たせいで痛い目にあったことがあります。夜間に動かしていたデータ取り込みのスクリプトが、エラーを表示しながらも終了コード0で終わっていたのです。

例外をexceptで受け止めてprintするだけにしていたため、外から見ると毎回成功していました。結局、数日分のデータが抜けていることに気づいたのは、利用者からの問い合わせがきっかけでした。

自動実行の仕組みや監視ツールの多くは、メッセージの中身ではなく終了コードで成否を判断します。人が画面を見ていない場面ほど、失敗したら0以外を返すことが大切です。

それ以来、自分の書くスクリプトは必ず、最後にsys.exitで結果を返す形にしています。たった1行の違いですが、トラブルに早く気づけるかどうかを大きく左右します。

まとめ

sysは、いま動いているPython自身とやり取りするための標準ライブラリです。たくさんの機能がありますが、最初はよく使うものだけ覚えておけば十分です。

コマンドライン引数はsys.argvで受け取り、中身はすべて文字列だと意識すること。終了するときはsys.exitで、成功なら0、失敗なら0以外を返すこと。この2つを押さえるだけで、スクリプトの完成度がぐっと上がります。

モジュールが見つからないときはsys.path、バージョンを確かめたいときはsys.version_infoと、困ったときの調べ道具としても頼りになります。次に import sys を見かけたら、どの機能を使っているのか探してみてください。

ここまでお読みいただきありがとうございました。

参考情報

次のアクション

記事で学んだ内容を実際に動かしてみよう

Python WebAcademyでは、ブラウザ上でコードを書きながら基礎から実践まで体系的に学べます。

Python WebAcademyの学習画面

あわせて読む

関連記事

ブログ一覧へ

Python学習ロードマップ

まずはこの3講座から

記事で気になったテーマを、順番に手を動かしながら学べます。

ロードマップを見る