JEPA4Japan · チュートリアル

関数:手順を再利用できる道具にする

1,910文字 5分で読めます #Python

引数、戻り値、明確な約束を持つ関数として、繰り返す処理をまとめます。

コース進捗 コース目次 24レッスン中 24件を公開中

レシピをボタンにしよう

同じ手順を何度もコピーすると、プログラムは長く、壊れやすくなります。関数は、いくつかの手順を1つの名前にまとめます。ボタンのように、動きを一度だけ決め、必要なときに何度でも押せます。

  1. 定義する レシピを一度書く
  2. 呼び出す 名前のボタンを押す
  3. 受け取る 戻り値を再利用する
仕事を一度だけ定義し、何度も呼び出して、その結果を使います。

これまでにもprint()、input()、int()、range()を呼び出してきました。今度は自分の関数を定義し、第2章の計算ツールを小さな仕事へ分けます。

定義し、呼び出し、値を渡す

defは関数を作ります。インデントされた本体は、呼び出されるまで待ちます。

def greet_learner():
    print("Pythonの学習を始めましょう!")


greet_learner()
greet_learner()

出力:

Pythonの学習を始めましょう!
Pythonの学習を始めましょう!

定義はレシピ、greet_learner()は呼び出しです。calculate_total()やshow_plan()のように、仕事が伝わる名前を付けましょう。

レシピ内の一時的なラベルを仮引数(parameter)、呼び出しが渡す値を**実引数(argument)**と呼びます。

def describe_session(topic, minutes):
    print(f"{topic}を{minutes}分学習します。")


describe_session("関数", 35)

topicとminutesが仮引数、"関数"と35が実引数です。1番目の実引数は1番目の仮引数へ入るので、順番にも意味があります。

  1. 実引数が届く "関数", 35
  2. 仮引数で呼ぶ topic, minutes
  3. 本体が動く 呼び出したときだけ
  4. ローカル名は消える この呼び出しが終わった後
呼び出しごとに、その関数だけで使う一時的な名前ができます。

関数内で作った名前は、通常その関数だけで使うローカル名です。外で使うとNameErrorになります。必要な値は仮引数から入れ、役立つ結果はreturnで外へ渡します。

returnはprintではない

print()は人へ見せ、returnはプログラムへ値を返します。この違いは大切です。

def double_and_return(number):
    return number * 2


result = double_and_return(15)
print(result + 5)

出力:

35

返された整数を、次の計算でも使えました。表示だけの関数と比べます。

def double_and_show(number):
    print(number * 2)


result = double_and_show(15)
print(result)

出力:

30
None

最初の30は関数内の表示です。値を返さずに関数の最後へ着いたので、呼び出しの結果はNoneになりました。

  1. print() 人へ値を見せる
  2. return 呼び出し元へ値を渡す
  3. returnなし 結果はNone
数値が画面に見えても、その関数が数値を返したとは限りません。

関数の小さな**約束(contract)**は、何を受け取り、何を行い、何を返すかを伝えます。分かりやすい名前と1行のdocstringで、その約束を見せられます。

def calculate_total_minutes(minutes_per_day, days):
    """毎日の学習計画から合計分数を返す。"""
    return minutes_per_day * days

Pythonがreturnへ着くと、その呼び出しはすぐ終わります。同じブロックで、必ず通るreturnの後ろに置いたコードは実行されません。

小さな仕事で計算ツールを作ろう

小さなプログラムでも、入力は値を集め、処理は結果を返し、出力は人へ見せる、と役割を分けると分かりやすくなります。完全な標準機能のプロジェクトをstudy_time_functions.pyへ保存します。

def calculate_total_minutes(minutes_per_day, days):
    """毎日の学習計画から合計分数を返す。"""
    return minutes_per_day * days


def minutes_to_hours(total_minutes):
    """分で表した時間を時間単位に変えて返す。"""
    return total_minutes / 60


def show_plan(topic, minutes_per_day, days, total_minutes, total_hours):
    """一つの学習計画を読みやすく表示する。"""
    print()
    print(f"テーマ:{topic}")
    print(f"毎日の学習:{minutes_per_day}分")
    print(f"学習日数:{days}日")
    print(f"合計:{total_minutes}分")
    print(f"総学習時間:{total_hours:.1f}時間")


def main():
    topic = input("学習テーマ:")
    minutes_per_day = int(input("1日あたりの学習時間(分):"))
    days = int(input("学習日数:"))

    total_minutes = calculate_total_minutes(minutes_per_day, days)
    total_hours = minutes_to_hours(total_minutes)

    show_plan(topic, minutes_per_day, days, total_minutes, total_hours)


main()

実行してPython、30、7と入力し、最後の部分を確認します。

テーマ:Python
毎日の学習:30分
学習日数:7日
合計:210分
総学習時間:3.5時間

入力をせず、処理だけを確認することもできます。

print(calculate_total_minutes(45, 4))
print(minutes_to_hours(180))

出力が180と3.0になれば成功です。小さな計算関数は、このように簡単に確かめられます。

3つの小さなチャレンジと明確な約束

  1. 日数を分へ。 days_to_minutes(days, hours_per_day)を書いて結果を返します。days_to_minutes(3, 2)は360です。表示は関数の外で行います。
  2. 目標の助手。 target_met(actual, target)を書き、真偽値を返します。(29, 30)、(30, 30)、(31, 30)で試します。
  3. 再利用できるラベル。 session_label(topic, minutes)を書き、Python:40分のような文字列を返します。結果を保存して2回表示します。

注意点:実引数の数を間違えるとTypeErrorです。文字列をint()へ渡すとValueErrorになり、0日は掛け算では安全でも、日数で割る平均では使えません。後の章で既定値、型ヒント、入力検証を追加します。

文字列を扱う準備はできたかな?

  • 関数を定義し、後から呼び出せる
  • 仮引数と実引数を見分けられる
  • 複数の実引数を正しい順で渡せる
  • print()は表示、returnは値を返すと説明できる
  • 明示的なreturnがない関数の結果がNoneになると分かる
  • 一時的な名前を関数内に保てる
  • 入力、仕事、結果を短い約束として説明できる
  • 関数版の計算ツールで210分、3.5時間を出せる
  • 3つの小さなチャレンジを終えた

次章では、これらの小さな関数を再利用しながら、Unicode文字列を調べ、正規化し、検索し、分割し、結合します。