Pythonを使って、コマンドライン上で動作するスケジュール管理プログラムを作成します。 このプログラムでは以下の機能を実装します:
- スケジュールの追加
- スケジュールの表示
- スケジュールの削除
プログラム名はsch.py
(schはscheduleの最初の3文字)で、コマンドラインからシンプルに操作できるよう設計します。
スケジュール管理プログラムの設計
今回作成するプログラムの特徴は以下の通りです:
- コマンドライン引数で操作:
-a
: スケジュールの追加-l
: スケジュールの一覧表示-d
: スケジュールの削除
- データの保存と読み込み:
- JSONファイルを利用します。
- シンプルな構造:
- すべての関数は簡潔で明確な役割を持たせます。
argparseモジュールとは?
argparse
モジュールは、Pythonでコマンドライン引数を解析するための標準ライブラリです。
このモジュールを使うと、プログラムをコマンドラインから柔軟に操作できるようになります。
argparseの基本的な使い方
1. 引数パーサーを作成する
argparse.ArgumentParser
を使って、引数を処理するための「引数パーサー」を作成します。
引数パーサーは、ArgumentParserクラスのインスタンスです。
このパーサーは、コマンドライン引数を解釈し、プログラムで使いやすい形に変換する役割を持っています。
import argparse parser = argparse.ArgumentParser(description="コマンドライン引数の例")
description
引数: プログラムの概要を説明します。この説明は、ユーザーが--help
を指定したときに表示されます。
例えば、このコードを含むスクリプトをpython script.py --help
で実行すると、以下のようなヘルプメッセージが表示されます:
usage: script.py [-h] コマンドライン引数の例
2. 引数を定義する
次に、プログラムが受け取る引数を定義します。「引数パーサー」のadd_argument
メソッドを使って、引数の名前や動作を指定します。
parser.add_argument("-a", help="スケジュールを追加する")
-a
:- コマンドライン引数のショートスタイル(短い名前)。
- 実際には、ユーザーが
-a
に続けて値を指定します(例:-a value
)。
help
:- 引数の説明です。
--help
を実行したときに、この引数がどのような目的で使われるのか表示されます。
- 引数の説明です。
3. 引数を解析する
最後に、コマンドラインから渡された引数を解析し、その値を取得します。これにはparse_args
メソッドを使用します。
args = parser.parse_args()
args
は、ユーザーが指定した引数を保持するオブジェクトです。- 属性として各引数にアクセスできます(例:
args.a
)。
例えば、次のようなコマンドを実行した場合:
> py script.py -a "My Schedule"
プログラム内でargs.a
を確認すると、"My Schedule"
という文字列が取得できます。
プログラム全体のコード
以下に、sch.py
のコードを示します。
プログラムの説明はコードの後にあります。
このプログラムは、スケジュールの追加、一覧表示、削除の3つの機能を持っています。
import argparse import json import os DATA_FILE = "schedule.json" def load_data(): """スケジュールデータをロードします""" if os.path.exists(DATA_FILE): with open(DATA_FILE, "r") as f: return json.load(f) else: return [] def save_data(data): """スケジュールデータを保存します""" with open(DATA_FILE, "w") as f: json.dump(data, f, indent=4) def add_schedule(date, time, title): """スケジュールを追加します""" d = load_data() s = {"id": len(d) + 1, "date": date, "time": time, "title": title} d.append(s) save_data(d) print(f"スケジュールを追加しました: {date} {time} - {title}") def list_schedule(date): """指定された日付のスケジュールを一覧表示します""" d = load_data() new_id = d[-1]["id"] + 1 if d else 1 s = {"id": new_id, "date": date, "time": time, "title": title} if l: print(f"スケジュール一覧 ({date}):") for s in l: print(f"{s['id']}. {s['time']} - {s['title']}") else: print(f"{date}のスケジュールはありません。") def delete_schedule(schedule_id): """指定されたIDのスケジュールを削除します""" d = load_data() d = [s for s in d if s["id"] != schedule_id] save_data(d) print(f"スケジュール(ID: {schedule_id})を削除しました。") def main(): """メイン関数: コマンドライン引数を解析し、処理を分岐します""" parser = argparse.ArgumentParser(description="スケジュール管理プログラム") parser.add_argument("-a", nargs=3, metavar=("DATE", "TIME", "TITLE"), help="スケジュールを追加します") parser.add_argument("-l", metavar="DATE", help="スケジュールを一覧表示します") parser.add_argument("-d", type=int, metavar="ID", help="スケジュールを削除します") args = parser.parse_args() if args.a: add_schedule(args.a[0], args.a[1], args.a[2]) elif args.l: list_schedule(args.l) elif args.d: delete_schedule(args.d) else: parser.print_help() if __name__ == "__main__": main()
プログラムの説明
定数
定数とは常に同じ値を持ち、値を変えない変数のことをいいます。 Pythonは、言語的に定数を持っていないので、変数を定数としても使います。
- すべて大文字で記述:
DATA_FILE
のように、大文字の名前が慣習的に定数を意味する。 - 変更しない前提で利用: 定数として扱う変数は、コード中で変更されないように設計する。
すなわち、プログラマーがこのようなルールを守ることで、定数を使えるようになります。
DATA_FILE
は実際は変数なので値を書き換えられますが、それをすると混乱の元になるので、しないようにしてください。
関数の説明コメント
関数の説明がdef文の次の行にあります。 この説明に使われているのは、3個のダブルクォート(トリプルクォート)で囲んだ文字列です。 ダブルクォートの代わりにシングルクォートを使うこともできます。 トリプルクォートの文字列は複数行に渡って記述することができます。
プログラムの説明はコメント文(ハッシュマーク#
以後がコメントになる)を使うのが普通ですが、ここで文字列を使っている理由を説明します。
この文字列はDocstringと呼ばれます。 Docstringは、Pythonのコードに関する説明やドキュメントを記述するための文字列です。 クラス、関数、メソッド、モジュールなどの冒頭に記述され、コードの動作や使い方を明確に伝えるために利用されます。 Docstringにはトリプルクォートを使います。
- この文字列は、関数などの
__doc__
属性に保存される - Docstringは
help
関数で参照することができる - ドキュメント生成ツール(例: Sphinx)で利用される
ハッシュマークのコメント文を使うと、そのコメントは実行時に無視されてしまうので、関数などの説明には必ず文字列を使ってください。 ドキュメント生成ツールについては、今後別の記事で説明する予定です。
load_data()
とsave_data()
- 役割: スケジュールデータを読み込みまたは保存します。ファイル形式はJSONです。
- ポイント:
- JSON形式のデータは、Pythonの辞書やリストに自然に対応
- ファイルが存在しない場合に備え、デフォルトで空のリストを返す
- jsonモジュールの使い方は、Python初級者のお勉強ノート(17)jsonモジュールを参照してください。
add_schedule(date, time, title)
- 役割: 新しいスケジュールをデータに追加します。
- ポイント:
- ローカル変数
d
を使ってスケジュールリストを管理。 s
は新規スケジュールを表す辞書。- 新しいidの作成部分は次の「条件式」の項を参照。
- ローカル変数
条件式
Pythonには式の中に条件を含む「条件式」があります。
C言語の三項演算子(A ?
B :
C)と同等のものですが、構文が違います。
<Trueのときの値> if <条件> else <Falseのときの値>
この構文は次のように動作します。
- <条件>を評価。
- 条件がTrueの場合、<Trueのときの値>が選ばれ、その値が返される。
- 条件がFalseの場合、<Falseのときの値>が選ばれ、その値が返される。
if文との違い
- 通常のif文は、条件に応じてスイート内のコードを実行する。文であって式ではないので、値を返すことはしない。
- 条件式は値を返す。同様のことはif文を使ってもできるが、複数行にわたり長くなる。条件式を使えば1行でシンプルに記述できる。
プログラム中の条件式の動作
new_id = d[-1]["id"] + 1 if d else 1
スケジュールのリストd
を評価する。
このとき、リストが空ならFalse、そうでなければTrueと判定します(このあと詳しく説明)。
したがって
- リストが空でなければ、リストの最後のidに1を加えたものを
new_id
に代入する。 - リストが空ならば、1を
new_id
に代入する。
Pythonのif文などの式評価では、bool型オブジェクト(TrueとFalse)以外も使えます。 以下の値がFalse(偽)として評価されます(「Falsy」とも呼ばれる)。
- None
- 数値の0(0, 0.0, 0jなど)
- 空のシーケンス([], "", ())
- 空のマッピング({})
- False
それ以外の値はすべてTrue(真)として評価されます(「Truthy」とも呼ばれる)。
list_schedule(date)
- 役割: 指定された日付のスケジュールを表示します。
- ポイント:
- リスト内包表記(後述)を使用して、条件に一致するスケジュールを抽出。
- 結果が空の場合、適切なメッセージを表示します。
リスト内法表記
Pythonのリスト内包表記は、簡潔で読みやすい方法でリストを作成するための構文です。 1行でリストの生成ができます。
リスト内包表記の基本構文
以下が基本的な構文です:
[<式> for <変数> in <イテラブル> if <条件>] # 条件部分は省略可能
<式>
: リストに追加される要素を表します。<変数>
:for
ループで使われる変数です。<イテラブル>
: リストや文字列、範囲などの反復可能オブジェクトです。if <条件>
: 任意の条件を指定できます。この条件を満たす場合のみリストに要素が追加されます。
sch.py
では、リスト内包表記を使ってスケジュールを抽出しています。
l = [s for s in d if s["date"] == date]
s for s in d
:- リスト
d
の各要素(s
)を走査します。
- リスト
if s["date"] == date
:- スケジュールの
"date"
が指定されたdate
と一致する場合のみ、s
をリストに追加します。
- スケジュールの
- 結果:
- 条件に一致するスケジュールのリストが作成されます。
同じ処理を通常のfor
ループで書くこともできますが、1行では書けません。
l = [] for s in d: if s["date"] == date: l.append(s)
リスト内包表記を使うことで、これを1行で簡潔に記述できます。
リスト内包表記の利点
- 簡潔で読みやすい:
- 1行でリストを生成でき、コードが短くなります。
- 高速:
- 内部で最適化されているため、通常の
for
ループより高速に動作する場合があります。
- 内部で最適化されているため、通常の
- 柔軟性:
- 条件式や複雑な処理を組み込むことができます。
リスト内包表記は糖衣構文として実装されています。 ここで、糖衣構文について簡単に説明します。
糖衣構文とは、プログラムの処理内容を簡潔に書けるようにするための表記方法です。 糖衣構文は新しい文法を追加したわけではなく、実際にはそれを同等な通常の構文に変換してPythonが解釈する仕組みです。 例えば、sch.pyの糖衣構文は、forループに書き直してPythonに解釈、実行されます。
リスト内包表記
l = [s for s in d if s["date"] == date]
展開されたforループ
l = [] for s in d: if s["date"] == date: l.append(s)
糖衣構文の特徴
- プログラムの本質には影響を与えない:
- 糖衣構文は、単にコードを簡潔に記述できる手段であり、実行される処理内容は通常の構文と同じです。
- 新しい機能ではなく既存の仕組みの簡略化:
- 糖衣構文は新しいPythonの文法ではなく、既存の機能を別の形で書きやすくしたものです。
- 例: リスト内包表記:
- Pythonがリスト内包表記をサポートするのは、
for
ループとif
文を簡潔に記述できるようにするためです。
なお、糖衣構文はPythonに組み込まれているもので、プログラマーが糖衣構文を作ることはできません。
delete_schedule(schedule_id)
- 役割: 指定されたIDのスケジュールを削除します。
- ポイント:
- 削除後のデータリストを更新する際にもリスト内包表記を活用。
main()
- 役割: コマンドライン引数を解析し、適切な関数を呼び出します。
- ポイント:
-a
,-l
,-d
といったオプションを指定し、それに応じた処理を実行。argparseモジュールの補足説明は次の項で行います。- 引数が不足している場合はヘルプメッセージを表示します。
argparseモジュールについての補足説明
1. -a
引数
parser.add_argument("-a", nargs=3, metavar=("DATE", "TIME", "TITLE"), help="スケジュールを追加します")
オプションの意味
-a
:- ショートオプション。コマンドラインで
-a
を指定することで、この引数を利用できます。
- ショートオプション。コマンドラインで
nargs=3
:- この引数は3つの値(引数)を必要とします。
- たとえば、
python sch.py -a 2023-12-10 14:00 "Meeting"
とすると、2023-12-10
、14:00
、Meeting
がそれぞれ別々に渡されます。
metavar=("DATE", "TIME", "TITLE")
:
-a DATE TIME TITLE スケジュールを追加します
help="スケジュールを追加します"
:- ヘルプメッセージに表示されるこの引数の説明です。
2. -l
引数
parser.add_argument("-l", metavar="DATE", help="スケジュールを一覧表示します")
オプションの意味
-l
:- ショートオプション。コマンドラインで
-l
を指定することで、この引数を利用できます。
- ショートオプション。コマンドラインで
metavar="DATE"
:- ヘルプメッセージで、この引数に入力するべき値を
DATE
として表示します。 - 例えば、
python sch.py --help
で次のように表示されます:
- ヘルプメッセージで、この引数に入力するべき値を
-l DATE スケジュールを一覧表示します
help="スケジュールを一覧表示します"
:- ヘルプメッセージに表示されるこの引数の説明です。
3. -d
引数
parser.add_argument("-d", type=int, metavar="ID", help="スケジュールを削除します")
オプションの意味
-d
:- ショートオプション。コマンドラインで
-d
を指定することで、この引数を利用できます。
- ショートオプション。コマンドラインで
type=int
:- この引数に指定される値を整数型に変換します。
- ユーザーが文字列や浮動小数点数などの無効な値を入力すると、自動的にエラーになります。
metavar="ID"
:- ヘルプメッセージで、この引数に入力するべき値を
ID
として表示します。 - 例えば、
python sch.py --help
で次のように表示されます:
- ヘルプメッセージで、この引数に入力するべき値を
-d ID スケジュールを削除します
help="スケジュールを削除します"
:- ヘルプメッセージに表示されるこの引数の説明です。
まとめ
nargs
:- 必要な引数の個数を指定します(例:
nargs=3
は3つの値が必要)。
- 必要な引数の個数を指定します(例:
metavar
:- ヘルプメッセージで引数の説明をわかりやすくするためのプレースホルダーを設定します。
type
:- 引数の型を指定し、不適切な型の入力を自動的にチェックします。
help
:- 引数の用途を説明する文字列で、
--help
実行時に表示されます。
- 引数の用途を説明する文字列で、
実行例
- スケジュールの追加
> py sch.py -a 2023-12-10 14:00 "ミーティング" スケジュールを追加しました: 2023-12-10 14:00 - ミーティング
- スケジュールの一覧表示
> py sch.py -l 2023-12-10 スケジュール一覧 (2023-12-10): 1. 14:00 - ミーティング
- スケジュールの削除
> py sch.py -d 1 スケジュール(ID: 1)を削除しました。
まとめ
本記事では、Pythonを使ったスケジュール管理プログラムを作成しました。
このプログラムは、コマンドラインツールの作成方法の例となります。 しかし、これだけで十分なわけではなく、テスト、パッケージ化なども必要です。 それらは、次回以降の記事で取り上げたいと思います。