Python | 2週間で身につく、アプリを作りながら学ぶPythonの基本 - 11日目

Python Python
スポンサーリンク

11日目のゴールとテーマ

11日目のテーマは「クラスとオブジェクトで“自分だけの型”を作る」です。
ここまで、辞書とリストを組み合わせて、かなり複雑なデータも扱えるようになりました。

でも、そろそろこう感じていませんか?

「毎回 {"name": ..., "deadline": ...} って書くの、ちょっとゴチャつく」
「タスクって、もっと“タスクらしいまとまり”として扱えないかな」

今日はここに一歩踏み込んで、

クラス(class)とオブジェクト(インスタンス)のイメージ
__init__ で「自分だけの型の“初期化”」を作る
メソッドで「その型専用の関数」を持たせる
タスク管理アプリの「タスク」をクラス化してみる

ここまでを狙います。


クラスとオブジェクトをイメージでつかむ

「設計図」と「そこから作られた実物」

まずは言葉のイメージから整えます。

クラス(class)は「設計図」です。
オブジェクト(インスタンス)は「その設計図から作られた実物」です。

例えば、「タスク」という概念を考えてみます。

タスクには、名前がある
締切がある
「あと何日か」を計算したい
「期限切れかどうか」を知りたい

こういう「タスクというものの性質」をまとめた“設計図”がクラスです。
そこから実際に「レポートを書く」「買い物に行く」といった具体的なタスクを作ったものが、オブジェクト(インスタンス)です。

辞書は「その場その場で自由に作るメモ」
クラスは「ちゃんと決めたフォーマットを持つ型」

こんなイメージを持っておくと、しっくり来やすいです。


いちばん小さなクラスを書いてみる

class と __init__ の基本形

まずは、超シンプルなクラスから始めます。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
Python

これが「Person という型の設計図」です。

ここで出てきたポイントを丁寧に見ていきます。

class Person:
これが「Person というクラスを定義します」という宣言です。
クラス名は、慣習的に先頭を大文字にします。

def __init__(self, name, age):
これが「コンストラクタ」と呼ばれる特別なメソッドです。
オブジェクトが作られるときに、自動的に呼ばれます。
__init__ は「イニシャライズ(初期化)」の意味です。

self.name = name
self.age = age
ここで、「このオブジェクトが持つ名前」と「年齢」をセットしています。
self は「今まさに作っている(または操作している)このオブジェクト自身」を指します。

このクラスからオブジェクトを作るには、こう書きます。

person1 = Person("太郎", 20)
person2 = Person("花子", 18)

print(person1.name, person1.age)
print(person2.name, person2.age)
Python

実行結果はこうなります。

太郎 20
花子 18

ここでの超重要ポイントは、

Person("太郎", 20) と書いた瞬間に、
内部で __init__(self, "太郎", 20) が呼ばれている

ということです。

self には「今作っている person1(や person2)」が自動で入ります。
だから、self.name = name と書くと、
「この人の name に ‘太郎’ を入れる」という意味になります。


メソッドで「その型専用の動き」を持たせる

関数との違いは「self を受け取るかどうか」

クラスの中には、__init__ 以外にも関数(メソッド)を書くことができます。

例えば、Person に「自己紹介する」メソッドを持たせてみましょう。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        print("私は" + self.name + "です。年齢は" + str(self.age) + "歳です。")
Python

使う側はこうなります。

person = Person("太郎", 20)
person.introduce()
Python

実行結果はこうです。

私は太郎です。年齢は20歳です。

ここでのポイントは、

クラスの中の関数は、第一引数に必ず self を取る
呼び出すときは person.introduce() のように書き、self は自動で渡される

ということです。

外側に書く普通の関数は、

def introduce(person):
    ...
Python

のように、「誰についての処理か」を引数で受け取ります。
クラスのメソッドは、「自分自身(self)」を暗黙に持っているので、
self.nameself.age に直接アクセスできます。

「データ(name, age)と、そのデータに関する処理(introduce)をひとまとめにしたもの」
これがクラスの強みです。


タスクをクラスとして定義してみる

辞書から「Task クラス」へ

ここからが今日の本番です。
これまでタスクは、こんな辞書で表現していました。

task = {"name": "レポートを書く", "deadline": datetime.date(2026, 4, 10)}
Python

これをクラスにしてみます。

import datetime


class Task:
    def __init__(self, name, deadline):
        self.name = name
        self.deadline = deadline

    def remaining_days(self):
        today = datetime.date.today()
        diff = self.deadline - today
        return diff.days

    def status_text(self):
        days = self.remaining_days()
        deadline_text = self.deadline.strftime("%Y/%m/%d")

        if days > 0:
            return "締切: " + deadline_text + "(あと " + str(days) + " 日)"
        elif days == 0:
            return "締切: " + deadline_text + "(今日が締切!)"
        else:
            return "締切: " + deadline_text + "(期限切れ: " + str(-days) + " 日過ぎ)"
Python

この Task クラスの中身を、じっくり分解します。

__init__(self, name, deadline)
タスクを作るときに、「名前」と「締切日」を受け取って、
それを self.nameself.deadline に保存しています。

remaining_days(self)
このメソッドは、「締切まであと何日か」を計算して返します。
中で self.deadline を使っているのがポイントです。
どのタスクかによって締切が違うので、「自分自身の締切」を使っています。

status_text(self)
このメソッドは、「締切の状態を表す文字列」を返します。
中で remaining_days() を呼んでいるのもポイントです。
同じクラスの中のメソッド同士は、self.メソッド名() で呼び合えます。

この Task クラスを使うと、こう書けます。

task = Task("レポートを書く", datetime.date(2026, 4, 10))

print(task.name)
print(task.deadline)
print(task.remaining_days())
print(task.status_text())
Python

辞書のときと比べて、

task["name"] ではなく task.name
task["deadline"] ではなく task.deadline

と書けるようになり、
「タスクらしさ」がコードににじみ出てきます。


タスクのリストを「Task オブジェクトのリスト」に変える

使い方は辞書のリストとほぼ同じ

Task クラスができたら、あとは「辞書の代わりに Task を使う」だけです。

これまでのタスクリストはこうでした。

tasks = [
    {"name": "レポートを書く", "deadline": datetime.date(2026, 4, 10)},
    {"name": "買い物に行く", "deadline": datetime.date(2026, 3, 30)}
]
Python

これをこう書き換えます。

tasks = [
    Task("レポートを書く", datetime.date(2026, 4, 10)),
    Task("買い物に行く", datetime.date(2026, 3, 30))
]
Python

一覧表示も、こう変わります。

def print_tasks(tasks):
    print("=== タスク一覧 ===")

    if len(tasks) == 0:
        print("タスクは登録されていません。")
        return

    for i, task in enumerate(tasks, start=1):
        print(str(i) + ". " + task.name + " → " + task.status_text())
Python

ここでの重要ポイントは、

「辞書のときに task["name"]task["deadline"] と書いていた部分が、
task.nametask.status_text() に変わっているだけ」

ということです。

内部表現が辞書からクラスに変わっても、
「タスクのリストを for で回す」という構造は同じです。
だから、クラスを導入しても、これまでの知識はそのまま活きます。


ファイル保存・読み込みを Task クラス対応にする

「外に出すときは文字列」「中に戻すときは Task」

7日目・10日目で作った「ファイル保存・読み込み」を、Task クラス対応にしてみます。

保存側はこうです。

def save_tasks_to_file(tasks, filename):
    with open(filename, "w", encoding="utf-8") as file:
        for task in tasks:
            line = task.name + "," + task.deadline.isoformat() + "\n"
            file.write(line)

    print("タスクをファイルに保存しました。")
Python

読み込み側はこうです。

def load_tasks_from_file(filename):
    tasks = []

    try:
        with open(filename, "r", encoding="utf-8") as file:
            for line in file:
                line = line.strip()
                if line == "":
                    continue

                parts = line.split(",")
                if len(parts) != 2:
                    continue

                name = parts[0]
                date_text = parts[1]

                deadline = parse_date(date_text)
                if deadline is None:
                    continue

                task = Task(name, deadline)
                tasks.append(task)

        print("タスクをファイルから読み込みました。")

    except FileNotFoundError:
        print("ファイルが見つかりませんでした。新しいタスク一覧を作ります。")
        tasks = []

    return tasks
Python

ここでのポイントは、

ファイルに書くときは、「Task を文字列に変換している」
ファイルから読むときは、「文字列から Task を作り直している」

ということです。

クラスを使っていても、「ファイルに書けるのは文字列だけ」というルールは変わりません。
だから、

Task(オブジェクト) → 文字列(保存用)
文字列 → Task(オブジェクト)

という変換を、自分で決めて書いています。


11日目のミニアプリ:Task クラス版タスク管理

10日目のアプリを「クラス対応」にする

ここまでの内容をまとめて、
10日目のタスク管理アプリを「Task クラス版」にしたイメージを示します。

全体像はこういう構成になります。

Task クラス(name, deadline, remaining_days, status_text)
parse_date 関数
add_task 関数(Task を作ってリストに追加)
print_tasks 関数(Task のメソッドを使って表示)
save_tasks_to_file / load_tasks_from_file 関数
print_menu / input_menu_number / main

コードは長くなるので、ここでは要所だけ押さえますが、
大事なのは「辞書を Task に置き換えるだけで、構造はほぼそのまま」ということです。


クラスを使うメリットを言葉で整理する

「データと、そのデータに関する処理をひとまとめにできる」

ここで一度、クラスのメリットを言葉で整理しておきます。

タスクという概念には、

名前
締切
締切までの日数
締切の状態(あと何日か、期限切れか)

といった「データ」と「ロジック」がセットで存在します。

辞書+関数の世界では、

データは辞書
ロジックは外側の関数

としてバラバラに持っていました。

クラスの世界では、

データは self.name, self.deadline
ロジックは remaining_days, status_text

として、ひとつの「Task 型」にまとめられます。

これによって、

タスクに関する処理が散らばらない
コードを読むときに、「タスクって何ができるのか」がクラスを見れば分かる
将来、タスクに新しい機能(例えば「完了フラグ」や「優先度」)を足したくなったときに、Task クラスを中心に変更すればよい

というメリットが生まれます。


11日目で一番大事な感覚

「クラスは“自分で作る新しい型”」

今日あなたに持ってほしい感覚はこれです。

int や str や list や dict は、Pythonが最初から持っている「型」です。
でも、あなたはもう「自分で型を作れる」段階に来ています。

Task
Person
Book
TodoItem
Account

あなたのアプリの中に出てくる「ものたち」を、
クラスとして定義していくことで、
コードは「ただの処理の集まり」から「登場人物のいる物語」に変わっていきます。

クラスは奥が深いですが、
今日やった

class クラス名:
__init__ で初期化
self.変数 で「そのオブジェクトの状態」を持つ
def メソッド(self, ...) で「そのオブジェクト専用の処理」を書く

この4つが押さえられていれば、
もう立派に「クラスを使う人」です。


11日目のまとめ

今日のキーポイントを短く整理すると、こうなります。

クラス(class)は「設計図」、オブジェクト(インスタンス)は「そこから作られた実物」。
__init__ は「オブジェクトが作られるときに呼ばれる初期化メソッド」。
self は「今扱っているこのオブジェクト自身」を指すキーワード。
Task クラスを作ることで、「タスクのデータ」と「タスクに関する処理」をひとまとめにできる。
辞書のリストは、そのまま「Task オブジェクトのリスト」に置き換えられる。

もし余裕があれば、今日の Task クラスに次のような機能を足してみてください。

タスクに「完了フラグ(done)」を持たせて、mark_done メソッドで完了にする
is_overdue メソッドを作って、「期限切れかどうか」を True/False で返す
to_line / from_line のようなメソッドを作って、「ファイル用の文字列との変換」をクラス側に持たせる

ここまで来ると、いよいよ「オブジェクト指向プログラミング」の入口に立っています。
残りの日数で、クラスを使ったアプリの形を、もう少しだけ深めていきましょう。

タイトルとURLをコピーしました