【第8回】関数入門:ログ処理に名前をつけてスッキリさせる

Python入門

第7回では、while文とinput()を使って、

  • q が押されるまで、コーヒーや釣りのログを入力し続ける
  • 入力されたログをCSVに追記する

という、小さな「ログ入力ツール」を作りました。

ただ、コードを見直してみると、こんな感覚が出てきませんか?

  • CSVに書き込むところ、何度も同じようなコードを書いているな…
  • ログ1件分の辞書を作るところも、かなりパターンが似ている

この 「同じような処理をまとめたい」 という感覚は、
関数(function) に進むタイミングの合図です。

今回のテーマは、

よく使う処理に名前をつけて、コードをスッキリさせること。

コーヒー&釣りログのコードを実際にリファクタしながら、

  • 関数の基本(def / 引数 / return)
  • 関数に「1つの役割」を持たせる考え方
  • 後で例外処理(try / except)やクラスを組み込むための土台

を、一緒に作っていきます。


この回のゴールと全体像

第8回のゴールは、次の4つです。

  1. 関数とは何か(「処理に名前をつける箱」)というイメージが持てる
  2. def / 引数 / return の基本的な使い方がわかる
  3. コーヒー&釣りログのコードから「関数に切り出せる部分」を見つけて整理できる
  4. 「関数の中で try / except を使って、エラー処理をまとめる」イメージが持てる

この先、

  • 第9回:例外処理入門(エラーと仲良くなる回)
  • 第10回:クラス超入門(ログをオブジェクトとして扱う)

に進んでいくための土台にもなります。

関数とは? よく使う処理にラベルを貼る

関数はひと言でいうと、

「よく使う処理に名前をつけたもの」

です。

例えば、コーヒーを淹れるときの手順をノートに書くと、

  • 豆を量る
  • 挽く
  • お湯を沸かす
  • 蒸らす
  • 少しずつお湯を注ぐ

という細かいステップになりますが、

会話の中ではまとめて

「コーヒー淹れてくるね」

で済ませますよね。

関数もこれと同じで、

  • 細かい処理のステップを1つにまとめて
  • 「この名前を呼んだら、まとめてやってね」

とPythonに頼むための仕組みです。

関数の基本形

def 関数名(引数1, 引数2, ...):
    処理
    return 結果
  • def:関数を定義しますよ、という合図
  • 関数名:その処理に付ける名前
  • 引数:外から受け取る値(材料)
  • return:外に返す値(結果)

という構造になっています。

例:スコアからコメントを返す関数

def comment_for_score(score):
    if score >= 4.5:
        return "かなり特別な一杯。記念すべきレベル。"
    elif score >= 4.0:
        return "安定して美味しい。リピートしたい。"
    elif score >= 3.0:
        return "普通に美味しい。気分次第でまた飲むかも。"
    else:
        return "好みとは少し違うかもしれない。"

使う側はこうです。

score = 4.2
comment = comment_for_score(score)
print("スコア:", score)
print("コメント:", comment)

中の if 文がどれだけ長くても、
呼び出し側から見るとたった1行で済むようになります。

まずは「ファイル1つ」で完結する関数の例

いきなりコーヒーログに組み込むとイメージがつきにくいので、
まずは 1つのファイルの中だけで完結する、いちばん小さい関数の例 を見てみます。

sample_function.py を作って実行してみる

VSCodeで、新しく sample_function.py というファイルを作り、次のコードを書きます。

def hello():
    print("こんにちは、関数の世界!")


# ここから下が「実行される部分」
hello()

ポイント:

  • 上の def hello():関数を定義
  • 下の hello() の行で 関数を呼び出す(実行する)

ターミナルで、このファイルがあるフォルダに移動してから:

python3 sample_function.py

を実行すると、

こんにちは、関数の世界!

と表示されます。

この流れが関数の最小パターンです。

  1. 上の方で def ... で関数を「用意」しておく
  2. 下の方で 関数名() と書いて「実行」する

引数と return つきの関数も同じ流れで動く

もう少しだけ進んだ例も見てみます。

def add(a, b):
    result = a + b
    return result


x = add(3, 5)
print("3 + 5 の結果は:", x)

やっていることは、

  • add という名前の関数を定義
  • ab という 引数(外から渡す値) を受け取る
  • return で「計算した結果」を呼び出し元に返す

というだけです。

実行方法はさきほどと同じで、

python3 sample_function.py

のように python ファイル名.py でOKです。
これがわかっていれば、このあとの「コーヒーログを関数で整理する」の実行もイメージしやすくなります。


1件分のコーヒーログを作る処理を関数にする

第7回の coffee_input.py では、
while ループの中にこういう処理を書いていました。

name = input("コーヒー名を入力してください(終了するには q):")
# ...
roast = input("焙煎度:")
memo = input("ひとことメモ:")
score_str = input("スコア(0〜5の数字):")
# try / except で score を float に変換
# log 辞書を作って append

これを「1件分のコーヒーログを作る関数」として切り出してみます。

入力 → 変換 → 辞書 にする関数

def input_one_coffee_log():
    """1件分のコーヒーログを入力して辞書にして返す関数"""

    name = input("コーヒー名を入力してください(終了するには q):")
    if name == "q":
        return None  # 終了の合図として None を返す

    roast = input("焙煎度(浅煎り・中煎り・深煎りなど):")
    memo = input("ひとことメモ:")
    score_str = input("スコア(0〜5の数字):")

    # ここで本当は try / except をしっかり書きたい。
    # 第7回では軽く触れましたが、例外処理は第9回で1話分じっくり扱う予定です。
    try:
        score = float(score_str)
    except ValueError:
        print("数値に変換できなかったので、スコアを 3.0 にします。")
        score = 3.0

    log = {
        "name": name,
        "roast": roast,
        "memo": memo,
        "score": score
    }

    return log

こうしておくと、while ループ側はかなりスッキリします。

while ループ側から見るとこうなる

coffee_logs = []

while True:
    log = input_one_coffee_log()

    if log is None:
        print("入力を終了します。")
        break

    coffee_logs.append(log)
    print("1件ログを追加しました。")
    print("----------------------")

入力 → 辞書作成 → スコア変換 → ログ作成
までのゴチャゴチャした処理は関数の中に隠れます。

while 側は「1件ログをもらう/None なら終わる」という
ストーリーだけ を読めばよくなります。

CSVに書き込む処理も関数にする

同じように、CSVに書き込む部分も
「ファイル名・フィールド名・ログのリスト」を受け取って書くだけの関数
にすると再利用しやすくなります。

複数のログをCSVに追記する関数

import csv
import os


def append_logs_to_csv(filename, fieldnames, logs):
    """ログのリストをCSVに追記する関数"""

    if not logs:
        print("書き込むログがありません。")
        return

    file_exists = os.path.exists(filename)

    with open(filename, "a", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)

        # ファイルがまだなければヘッダーを書く
        if not file_exists:
            writer.writeheader()

        writer.writerows(logs)

    print(f"{filename} に {len(logs)} 件のログを書き込みました。")

コーヒーログ側から使う

# coffee_functions.py などにまとめてもよい

FIELDNAMES_COFFEE = ["name", "roast", "memo", "score"]

coffee_logs = []

while True:
    log = input_one_coffee_log()
    if log is None:
        break
    coffee_logs.append(log)

append_logs_to_csv("coffee_logs.csv", FIELDNAMES_COFFEE, coffee_logs)

コーヒーログツールを関数で整理した全体像

ここまでバラバラに関数の例を見てきたので、
実際に 1つのファイルとして完成した形 を載せておきます。

ファイル名は coffee_logger.py としてみましょう。

# coffee_logger.py

import csv
import os


def input_one_coffee_log():
    """1件分のコーヒーログを入力して辞書にして返す関数"""

    name = input("コーヒー名を入力してください(終了するには q):")
    if name == "q":
        return None  # 終了の合図として None を返す

    roast = input("焙煎度(浅煎り・中煎り・深煎りなど):")
    memo = input("ひとことメモ:")
    score_str = input("スコア(0〜5の数字):")

    # ★ 本格的な例外処理(try / except)の話は第9回でじっくり扱います。
    try:
        score = float(score_str)
    except ValueError:
        print("数値に変換できなかったので、スコアを 3.0 にします。")
        score = 3.0

    log = {
        "name": name,
        "roast": roast,
        "memo": memo,
        "score": score,
    }

    return log


def append_logs_to_csv(filename, fieldnames, logs):
    """ログのリストをCSVに追記する関数"""

    if not logs:
        print("書き込むログがありません。")
        return

    file_exists = os.path.exists(filename)

    with open(filename, "a", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)

        # ファイルがまだなければヘッダーを書く
        if not file_exists:
            writer.writeheader()

        writer.writerows(logs)

    print(f"{filename} に {len(logs)} 件のログを書き込みました。")


def main():
    """このスクリプト全体のメインの流れ"""

    FIELDNAMES_COFFEE = ["name", "roast", "memo", "score"]
    filename = "coffee_logs.csv"
    coffee_logs = []

    while True:
        log = input_one_coffee_log()

        if log is None:
            print("入力を終了します。")
            break

        coffee_logs.append(log)
        print("1件ログを追加しました。")
        print("----------------------")

    append_logs_to_csv(filename, FIELDNAMES_COFFEE, coffee_logs)


# このファイルを「直接実行したときだけ」main() を呼ぶおまじない
if __name__ == "__main__":
    main()

coffee_logger.py の実行方法

VSCodeで coffee_logger.py を作り、上のコードを丸ごと貼る
ターミナルで、このファイルがあるフォルダに移動する
次のコマンドを実行:

  1. VSCodeで coffee_logger.py を作り、上のコードを丸ごと貼る
  2. ターミナルで、このファイルがあるフォルダに移動する
  3. 次のコマンドを実行:
python3 coffee_logger.py
  1. 画面の指示に従ってコーヒー名などを入力
  2. 入力をやめたいときは、コーヒー名のところで q と入力してEnter

実行が終わったあと、同じフォルダに coffee_logs.csv ができていれば成功です。
以降は何度でも python coffee_logger.py を実行して、
コーヒーを飲むたびにログを少しずつ積み上げていけます。


釣りログ用の「1件作る関数」と組み合わせる

釣りログでも、同じ append_logs_to_csv を再利用できます。

def input_one_fishing_log():
    """1件分の釣りログを入力して辞書にして返す関数"""

    date = input("日付(例:2025/05/01、終了するには q):")
    if date == "q":
        return None

    lake = input("場所(湖の名前など):")
    fish_type = input("魚種(ブラウン・レイクなど):")
    length_str = input("サイズ(cm):")
    is_released_str = input("リリースしましたか?(y/n):")

    try:
        length = int(length_str)
    except ValueError:
        print("整数に変換できなかったので、サイズを 0cm とします。")
        length = 0

    is_released = (is_released_str.lower() == "y")

    log = {
        "date": date,
        "lake": lake,
        "fish_type": fish_type,
        "fish_length_cm": length,
        "is_released": is_released
    }

    return log
FIELDNAMES_FISHING = ["date", "lake", "fish_type", "fish_length_cm", "is_released"]

fishing_logs = []

while True:
    log = input_one_fishing_log()
    if log is None:
        break
    fishing_logs.append(log)

append_logs_to_csv("fishing_logs.csv", FIELDNAMES_FISHING, fishing_logs)

こうして見ると、

  • 「コーヒー用」と「釣り用」の違いは
    • どんな項目を入力するか
    • 辞書にどんなキーを持たせるか
  • CSVに書き込むロジックそのものは共通

という構造がはっきり見えてきます。

関数を作ることで、「同じパターン」と「違い」が見つけやすくなります。

関数の中で try / except を使う意味

第7回・第8回では、一部のコードで

try:
    score = float(score_str)
except ValueError:
    print("数値に変換できなかったので、スコアを 3.0 にします。")
    score = 3.0

のように try / except を使いました。

今はまだ「ざっくり使っているだけ」ですが、
関数の中にこのようなエラー処理を閉じ込めておくと、使う側が楽になります。

エラー処理を「関数の中に隠す」イメージ

例えば、関数の外側から見れば、

log = input_one_coffee_log()

と書いているだけで、

  • input() で文字列を受け取る
  • float() / int() への変換でエラーが出るかもしれない
  • そのときにどうフォローするか

といった細かな判断は、関数の中に閉じ込められます。

この、

「エラーが起きるかもしれない細かいところは関数の中に押し込めて、
関数の外側は“きれいな物語”にしておく」

という考え方は、例外処理を学ぶときにも、クラスを学ぶときにも、
とても大事な土台になります。

例外処理を深か掘る

ここまでで、

  • 第7回:while の中で try / except を「ちょっとだけ」使う
  • 第8回:関数の中に try / except を閉じ込めるイメージを持つ

というところまできました。

ただ、「例外処理(エラー処理)」そのものは、
1話分ゆっくりやったほうが絶対に理解しやすいテーマです。

なので、このシリーズでは、

第9回を「例外処理入門」として解説します

予定している内容は、例えばこんな感じです。

  • 例外とは何か?エラーとどう違う?
  • try / except / else / finally の基本形
  • どこまでを「エラー」として扱い、どこからを「正常系」にするか
  • ログ入力ツールにおける「ユーザー入力のゆるい扱い方」
  • 例外を「飲み込む」のではなく、ログに残したり上に投げたりする考え方

関数を先に押さえたことで、

  • 「この関数の中で try / except をどう書くか」
  • 「呼び出し側はどう振る舞ってほしいか」

という視点で、例外処理の話がしやすくなりました。

さらにその先はクラスへと続く

関数までたどり着くと、次に見えてくるのが クラス(class) です。

これまでの流れを整理すると:

  1. 変数・リスト・辞書 → 「データ」を扱えるようになった
  2. if・for・while → 「流れ・条件・くり返し」を扱えるようになった
  3. 関数 → 「よく使う処理」をひとかたまりにして名前をつけられるようになった

ここからクラスに進むときの自然な一歩は、

「データ(ログ)と、そのデータに関する処理(メソッド)を、1つのまとまりとして扱いたい」

という欲求です。

例えば:

  • CoffeeLog というクラスを作って
    • プロパティとして name, roast, memo, score を持ち
    • to_dict() でCSV用の辞書を返す
    • comment() でスコアに応じたコメントを返す

こんなふうに、

  • 「1件分のログ」という“もの”
  • それに対する操作(処理)

をひとまとめにするのがクラスです。

関数の回を挟んだことで、

  • 「処理に名前をつけて外出しする」

という感覚ができています。

次にクラスを学ぶときは、

  • 「データ+そのデータ向けの関数」をまとめる箱

として捉えると、とてもスムーズに入っていけます。

今日のまとめ 関数でログツールが「少しだけ大人になる」

今回は、

  • 関数とは「よく使う処理に名前をつける箱」という話
  • def / 引数 / return の基本
  • 1件分のログ入力処理を input_one_coffee_log() / input_one_fishing_log() に切り出す
  • 複数ログをCSVに追記する append_logs_to_csv() を作る
  • 関数の中に try / except を閉じ込めるイメージ
  • 第9回で「例外処理入門」を1話まるごとやる計画
  • その先に「クラスでログをオブジェクトとして扱う」流れがあること

を見てきました。

同じコーヒー・同じブラウントラウトのログでも、

  • ただ動けばいいスクリプト
    から
  • 役割ごとに関数に分かれた、育てやすいツール

へと少し成長したはずです。

次回予告 – 第9回:例外処理入門「エラーと仲良くなる」

第9回は、

エラーと仲良くなる

をテーマに、

  • 例外(Exception)とは何か
  • try / except / else / finally の基本形
  • 「ここまではユーザーの入力ミスとしてやさしく扱う」
    「ここからは本当に危ないエラーだから止める」
    といった線引き
  • ログ入力ツールに「エラー時の振る舞い」をどう埋め込むか

を、コーヒー&釣りログのコードを使いながら丁寧に解説します。

例外処理を1話かけて押さえたあと、

  • データと処理をまとめる クラス超入門

に進むことで、

  • 「現実の世界(湖・コーヒー・日々の記録)」
    を、
  • 「Pythonの世界のオブジェクト」として扱えるようになっていきます。

引き続き、湖とコーヒーと星空のそばで、
少しずつPythonの世界を広げていきましょう。

コメント