パッケージ構成に整理
このレッスンでできるようになること
1本のファイルに詰め込んでいた家計簿を、models と report に分けたパッケージの形に整理できるようになります。
パッケージはフォルダにした module です
第1章で、関数を別ファイルへ移して import することを学びました。ファイルが3本4本と増えてくると、今度はそのファイル自体が散らかります。そこで関係するファイルをフォルダにまとめ、フォルダごと import できるようにしたものがパッケージです。
kakeibo/
__init__.py
models.py
report.py__init__.py は「このフォルダはパッケージです」という目印のファイルです。中身は空で構いません。この形にしておくと、外からは次のように呼べます。
from kakeibo.models import Expense
from kakeibo.report import category_totalskakeibo.models の . は、フォルダの中のファイルを指しています。パスの区切りが . になっただけで、やっていることは第1章の自作 module の import と同じです。
どう分けるかは責務で決めます
ファイル名で迷ったら、そのコードが何の役目を持っているかで分けます。今回の家計簿なら次の2つです。
models.py— データの形。第7章で作ったExpenseクラスを置きますreport.py— データを読んで数える処理。合計やカテゴリ別集計を置きます
この分け方の良いところは、Expense に項目を1つ足したくなったときに models.py だけを開けばよく、集計の書き方を変えたくなったときは report.py だけを開けばよいことです。逆に「表示の関数と集計の関数が同じファイルに混ざっている」と、直したい場所を探すところから始まります。
この演習では中身をコードで書き出します
ふだんはエディタで models.py を作りますが、この学習環境ではファイルを1本しか書けません。そこで中身を文字列として持っておき、プログラムからファイルに書き出してから import します。
from pathlib import Path
MODELS_SRC = """from dataclasses import dataclass
@dataclass
class Expense:
name: str
amount: int
category: str
date: str
"""
Path("kakeibo").mkdir(exist_ok=True)
Path("kakeibo/models.py").write_text(MODELS_SRC, encoding="utf-8")mkdir(exist_ok=True) は、すでにフォルダがあってもエラーにしない書き方です。write_text は第6章の pathlib で出てきたものです。
書き出した直後に import するときは、1行だけ添えるものがあります。
import importlib
importlib.invalidate_caches()Python は「どこにどのファイルがあるか」を一度調べたら覚えています。プログラムの実行中に新しく作ったフォルダは、その記憶に入っていません。invalidate_caches() はその記憶を捨てて、もう一度探し直させる命令です。この1行が無いと、たった今作ったばかりのパッケージが見つからず ModuleNotFoundError になります。
新しい文法はここまでです。あとは第7章までの道具で中身を書くだけです。
要件
- kakeibo フォルダを作り、init.py / models.py / report.py の3本を書き出す
- models.py に dataclass の Expense を定義する。属性は name, amount, category, date の4つ
- Expense に is_expensive メソッドを持たせる。amount が 1000 以上なら True を返す
- report.py に total_amount と category_totals を定義する。category_totals は Counter で返す
- 書き出したあと importlib.invalidate_caches() を呼んでから import する
- 支出は スーパー3200円食費, 電車代480円交通費, コンビニ850円食費, 技術書1600円教養 の4件を使う
- 1行目に 合計 6130円 の形で総額を表示する
- 2行目以降にカテゴリ名の昇順で 食費 4050円 の形で表示する
- 最後の行に 高額 2件 の形で is_expensive が True の件数を表示する
入出力例
出力: "合計 6130円
交通費 480円
教養 1600円
食費 4050円
高額 2件"