ここから第19回で、JSONへ保存できるタスク管理CLIを作ります。前提は第01〜16回です。操作はadd、list、doneです。
型注釈の先にある検証
タスクは正の整数ID、1〜200コードポイントの題名、真偽の完了状態を持ちます。題名の前後の空白を除き、改行とタブは認めません。doneを二度実行しても完了状態を保ち、存在しないIDはエラーとします。同時に複数プロセスが更新する用途は対象外です。
新しいフォルダへdomain.pyを保存します。
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class Task:
id: int
title: str
done: bool = False
def __post_init__(self):
if type(self.id) is not int or self.id <= 0:
raise ValueError("IDは正の整数です")
if type(self.title) is not str:
raise ValueError("題名は文字列です")
title = self.title.strip()
if not 1 <= len(title) <= 200 or any(c in title for c in "\t\n\r"):
raise ValueError("題名は改行・タブなしの1〜200文字です")
if type(self.done) is not bool:
raise ValueError("完了状態は真偽値です")
object.__setattr__(self, "title", title)
def complete(self):
return replace(self, done=True)
@classmethod
def from_dict(cls, data):
if not isinstance(data, dict) or set(data) != {"id", "title", "done"}:
raise ValueError("タスクの項目が不正です")
return cls(data["id"], data["title"], data["done"])
このファイルは次回のCLIからimportします。python -c "from domain import Task; print(Task(1, '復習').complete())" で、完了状態のTaskを確認できます。frozenな値の初期化時だけ、object.__setattr__で正規化した題名を設定しています。
入力の入口を一つにする
JSONから読み込む辞書も、CLIの文字列も、最終的には同じTaskの検査を通します。型注釈は実行時に自動で強制されないため、外部入力に必要な検査を明示しています。boolを整数IDとして通さない点にも注意してください。
保存形式はバージョン番号とtasks配列を持つJSONにします。将来項目が増えた場合、どの形式で保存したデータかを識別できるようにするためです。未知の項目を勝手に捨てて上書きすると、別の版が保存した情報を失うことがあります。この教材では未知の項目を拒否する方針です。
練習と解答
練習:IDがTrue、題名が空白だけ、doneが文字列の「false」の入力を試します。
解答:すべてValueErrorです。bool("false") はTrueになるため、真偽の変換へ安易に使えません。入力仕様に従い、受け入れる表記を列挙して変換するか、この例のようにJSONの本当の真偽値だけを認めます。
公式資料
dataclassesが参照先です。
Python全20回の目次 | 前の回 | 次の回