Pythonの関数を理解する|引数・戻り値・スコープ・型ヒントの基本

「Pythonの関数を理解する|引数・戻り値・スコープ・型ヒントの基本」の内容を表す技術イラスト

同じ計算式を何度も書く、入力処理と計算処理が混ざる、修正箇所が増える。このような問題を解決する基本的な手段が関数です。

関数は、ひとまとまりの処理へ名前を付け、必要な値を渡して呼び出せるようにしたものです。

たとえば油圧シリンダの推力計算を関数にすれば、入力値を変えながら同じ計算を再利用できます。

def calculate_force_n(pressure_mpa, area_mm2):
    force_n = pressure_mpa * area_mm2
    return force_n

この記事では、defによる関数定義から、引数、戻り値、スコープ、docstring、型ヒントまでを扱います。最後に、入力・計算・出力を分けた小さな工学計算プログラムを作ります。

目次

今日の到達点

この記事を読み終えると、次のことができるようになります。

  • defで関数を定義して呼び出せる
  • 引数と戻り値の役割を説明できる
  • print()とreturnの違いを理解できる
  • デフォルト引数とキーワード引数を使える
  • ローカル変数とグローバル変数を区別できる
  • docstringで関数の目的を残せる
  • 型ヒントで入力と出力の想定を表現できる
  • 入力・計算・出力を別の関数へ分割できる
  • 計算処理を再利用し、テストしやすい形にできる

defで関数を定義する

Pythonでは、defに続けて関数名と丸括弧を書きます。

def show_pressure(pressure_mpa):
    print(f"Pressure: {pressure_mpa:.1f} MPa")

この時点では関数を定義しただけです。処理を実行するには、関数名へ丸括弧を付けて呼び出します。

show_pressure(14.0)

実行結果です。

Pressure: 14.0 MPa

基本構造は次のとおりです。

def 関数名(パラメータ):
    実行する処理

Python公式チュートリアルでは、defの後に関数名と仮引数のリストを書き、関数本体をインデントすると定義されています。関数定義の公式解説を参照してください。

関数名には、その関数が何をするのか分かる動詞を含めると読みやすくなります。

calculate_force_n()
validate_pressure()
format_result()

単位が固定されている関数では、_nや_mpaのように単位を名前へ含める方法も有効です。

パラメータと引数

関数定義に書かれた名前をパラメータ、関数を呼び出すときに渡す具体的な値を引数と呼びます。

def calculate_force_n(pressure_mpa, area_mm2):
    return pressure_mpa * area_mm2

この定義では、pressure_mpaとarea_mm2がパラメータです。

force_n = calculate_force_n(14.0, 1000.0)

呼び出し時の14.0と1000.0が引数です。

実行結果を表示します。

print(force_n)
14000.0

関数の内部では、渡された値をパラメータ名で参照できます。

この例では次の単位関係を使用しています。

\[ 1\ \mathrm{MPa}=1\ \mathrm{N/mm^2} \]

したがって、

\[ F=pA \]

  • \(F\):推力 N
  • \(p\):圧力 MPa
  • \(A\):受圧面積 mm²

として計算できます。

returnで計算結果を返す

returnは、関数の呼び出し元へ値を返します。

def calculate_force_n(pressure_mpa, area_mm2):
    force_n = pressure_mpa * area_mm2
    return force_n

返された値は、変数へ代入したり、別の計算へ渡したりできます。

force_n = calculate_force_n(14.0, 1000.0)
force_kn = force_n / 1000
print(force_kn)

実行結果です。

14.0

returnが実行されると、その関数の処理は終了します。

def classify_pressure(pressure_mpa):
    if pressure_mpa > 16.0:
        return "over limit"
    return "within limit"

Pythonでは、returnを書かない関数もNoneを返します。Python公式チュートリアルでも、値を指定しないreturnと関数末尾への到達はNoneを返すと説明されています。

printとreturnは役割が違う

print()は値を画面へ表示します。returnは値を呼び出し元へ返します。

def print_force(pressure_mpa, area_mm2):
    force_n = pressure_mpa * area_mm2
    print(force_n)

この関数は結果を表示しますが、計算結果を返していません。

result = print_force(14.0, 1000.0)
print(result)

実行結果は次のようになります。

14000.0
None

再利用する計算関数では、画面表示ではなく値を返す方が扱いやすくなります。

def calculate_force_n(pressure_mpa, area_mm2):
    return pressure_mpa * area_mm2

呼び出し側で用途を選べます。

force_n = calculate_force_n(14.0, 1000.0)
print(force_n)

save_value = force_n
is_over_limit = force_n > 12000

計算と表示を分けることで、同じ関数を画面表示、CSV出力、JSON生成、テストなどへ再利用できます。

デフォルト引数

パラメータへ標準値を設定すると、呼び出し時にその引数を省略できます。

def calculate_effective_force_n(
    pressure_mpa,
    area_mm2,
    efficiency=1.0,
):
    return pressure_mpa * area_mm2 * efficiency

効率を省略すると1.0が使われます。

print(calculate_effective_force_n(14.0, 1000.0))

実行結果です。

14000.0

効率を指定すると、デフォルト値の代わりに指定値が使われます。

print(calculate_effective_force_n(14.0, 1000.0, 0.9))

実行結果です。

12600.0

デフォルト引数は、一般的な設定を省略可能にするための仕組みです。値の意味が不明確になるほど多用するのは避けます。

キーワード引数

引数をパラメータ名=値の形式で渡す方法をキーワード引数と呼びます。

force_n = calculate_effective_force_n(
    pressure_mpa=14.0,
    area_mm2=1000.0,
    efficiency=0.9,
)

引数が何を表すのか、呼び出し側でも確認できます。

位置だけで値を渡す次のコードは短く書けます。

force_n = calculate_effective_force_n(14.0, 1000.0, 0.9)

しかし、数値が複数並ぶと意味を取り違えやすくなります。

force_n = calculate_effective_force_n(
    pressure_mpa=14.0,
    area_mm2=1000.0,
    efficiency=0.9,
)

工学計算では、圧力、面積、効率、温度などの数値を誤った位置へ渡しても、Python上は計算できてしまう場合があります。重要な引数にはキーワード引数を使うと意図を明確にできます。

キーワード引数の順序は入れ替えられますが、存在しない名前を指定したり、同じパラメータへ複数回値を渡したりすることはできません。Python公式チュートリアルで呼び出し規則を確認できます。

変更可能な値をデフォルト引数にしない

リストや辞書などの変更可能なオブジェクトをデフォルト値へ直接指定すると、呼び出し間で同じオブジェクトが共有されます。

def add_result(value, results=[]):
    results.append(value)
    return results

この関数を繰り返し呼ぶと、前回の値が残ります。

print(add_result(10))
print(add_result(20))

結果は次のようになります。

[10]
[10, 20]

毎回新しいリストを作りたい場合は、Noneをデフォルト値にします。

def add_result(value, results=None):
    if results is None:
        results = []
    results.append(value)
    return results

実行結果です。

[10]
[20]

デフォルト値は関数を呼ぶたびではなく、関数が定義された時点で一度評価されます。この注意点はデフォルト引数の公式解説にも明記されています。

ローカルスコープとグローバルスコープ

変数を参照できる範囲をスコープと呼びます。

関数内で作った変数は、基本的にその関数のローカル変数です。

def calculate_force_n(pressure_mpa, area_mm2):
    force_n = pressure_mpa * area_mm2
    return force_n

force_nは関数の外側から直接参照できません。

print(force_n)

関数外でこのコードだけを実行すると、NameErrorになります。

一方、関数の外側で定義された名前はグローバルスコープに属します。

unit_name = "MPa"

def format_pressure(pressure_mpa):
    label = f"{pressure_mpa:.1f} {unit_name}"
    return label

print(format_pressure(14.0))

実行結果です。

14.0 MPa

関数内からグローバル変数を参照できますが、関数内でグローバルな状態を次々に変更する設計は、処理結果を追いにくくします。

次のように、必要な値は引数として渡し、結果は戻り値として受け取る方が関係を明確にできます。

def format_pressure(pressure_mpa, unit_name):
    return f"{pressure_mpa:.1f} {unit_name}"

Pythonでは関数を呼び出すとローカル名前空間が作られ、関数が終了すると破棄されます。名前の探索順序などはPython公式チュートリアルのスコープ解説で確認できます。

docstringで関数の目的を書く

関数本体の最初に書く文字列をdocstringと呼びます。

def calculate_force_n(pressure_mpa, area_mm2):
    """Return theoretical force in newtons."""
    return pressure_mpa * area_mm2

docstringには、コードを読んだだけでは判断しにくい情報を残します。

  • 関数が何を返すか
  • 入力値の単位
  • 計算の前提
  • 副作用の有無
  • 発生しうる例外

複数行でも記述できます。

def calculate_force_n(pressure_mpa, area_mm2):
    """Return theoretical force in newtons.

    pressure_mpa is pressure in MPa.
    area_mm2 is effective area in mm².
    """
    return pressure_mpa * area_mm2

docstringはhelp()やドキュメント生成ツールから参照できます。Python公式の規約では、最初の行を簡潔な要約にし、詳細を続ける場合は2行目を空行にします。Documentation Stringsを参照してください。

型ヒントで入力と出力を示す

型ヒントを使うと、パラメータと戻り値に想定する型を示せます。

def calculate_force_n(
    pressure_mpa: float,
    area_mm2: float,
) -> float:
    return pressure_mpa * area_mm2

この定義は次の意図を表します。

  • pressure_mpa:float
  • area_mm2:float
  • 戻り値:float

戻り値を返さない関数は、-> Noneと書けます。

def show_pressure(pressure_mpa: float) -> None:
    print(f"{pressure_mpa:.1f} MPa")

型ヒントは、関数の入出力を読む人や開発ツールへ伝える情報です。Pythonの実行環境が、注釈だけを根拠に型を強制するわけではありません。typing公式ドキュメントでも、実行時には型注釈を強制せず、型チェッカー、IDE、リンターなどが利用すると説明されています。

したがって、型ヒントを書いても入力検証は別途必要です。

def double_value(value: float) -> float:
    return value * 2

value: floatと書いただけでは、文字列の入力を自動的に拒否する処理にはなりません。

入力・計算・出力を分離する

すべての処理を1つの場所へ書くと、計算部分だけを再利用したり検証したりすることが難しくなります。

小さなプログラムでも、次の3つを分けると構造が明確になります。

  • 入力:外部から値を受け取る
  • 計算:値を検証して結果を求める
  • 出力:結果を表示用の文字列へ整える

油圧シリンダの理論推力を計算する例です。

\[ A=\frac{\pi D^2}{4} \]\[ F=pA\eta \]

  • \(D\):シリンダ内径 mm
  • \(A\):受圧面積 mm²
  • \(p\):圧力 MPa
  • \(\eta\):効率
  • \(F\):推力 N
import math


def calculate_cylinder_force_kn(
    pressure_mpa: float,
    bore_mm: float,
    efficiency: float = 1.0,
) -> float:
    """Return hydraulic cylinder force in kilonewtons."""
    if not math.isfinite(pressure_mpa) or pressure_mpa <= 0:
        raise ValueError(
            "pressure_mpa must be a finite positive value"
        )
    if not math.isfinite(bore_mm) or bore_mm <= 0:
        raise ValueError(
            "bore_mm must be a finite positive value"
        )
    if not math.isfinite(efficiency) or not 0 < efficiency <= 1:
        raise ValueError(
            "efficiency must be greater than 0 and at most 1"
        )

    area_mm2 = math.pi * bore_mm ** 2 / 4
    force_n = pressure_mpa * area_mm2 * efficiency
    return force_n / 1000


def format_force_result(
    part_name: str,
    force_kn: float,
) -> str:
    """Return a display string for a calculated force."""
    return f"{part_name}: {force_kn:.2f} kN"

実行結果です。

Cylinder A: 63.33 kN

このコードと、負数、0、範囲外効率、NaNを渡した検証分岐はPython 3.12.14で実行確認済みです。

calculate_cylinder_force_kn()は計算結果を返すだけで、画面表示を行いません。そのため、次のように計算部分だけを直接呼び出せます。

force_kn = calculate_cylinder_force_kn(
    pressure_mpa=14.0,
    bore_mm=80.0,
)
print(force_kn)

実行結果です。

70.37167544041138

必要に応じて表示、CSV保存、JSON変換、テストへ接続できます。

raise ValueErrorは、不正な入力を検出したときに処理を正常値として続行させないための記述です。例外の受け取り方はDay 8で詳しく扱います。

また、この計算は単純化した理論値です。実機の設計では、背圧、摩擦、圧力損失、負荷条件、安全率などを別途検討する必要があります。

関数を分ける基準

行数だけで関数を分割するのではなく、役割の境界を見ます。

関数へ分けやすい処理は次のとおりです。

  • 数式を使って値を求める
  • 入力値を検証する
  • 単位を変換する
  • 文字列を整形する
  • CSVの1行を変換する
  • 判定結果を返す

1つの関数が入力、計算、表示、ファイル保存をすべて担当すると、修正の影響範囲が広がります。

関数名を見て「何を受け取り、何を返すか」を説明できる程度の単位にすると、再利用しやすくなります。

よくある失敗

returnを書き忘れる

def calculate_force_n(pressure_mpa, area_mm2):
    force_n = pressure_mpa * area_mm2

計算していますが、結果を返していないため、呼び出し結果はNoneです。

def calculate_force_n(pressure_mpa, area_mm2):
    force_n = pressure_mpa * area_mm2
    return force_n

関数内で表示まで固定する

計算関数が常にprint()すると、結果を別の処理へ渡しにくくなります。計算は値を返し、表示は呼び出し側または別関数で行います。

単位の異なる引数を渡す

calculate_cylinder_force_kn(
    pressure_mpa=14_000_000,
    bore_mm=80.0,
)

圧力へPaの値を渡しても、型はfloatやintとして計算できてしまいます。型ヒントだけでは単位違いを検出できないため、変数名、docstring、入力検証で前提を明示します。

グローバル変数へ依存する

関数外の値を暗黙に読み書きすると、同じ引数でも外部状態によって結果が変わる可能性があります。必要な値は引数へ、結果は戻り値へ集約すると関係が明確になります。

関数を細かく分けすぎる

1行ごとに関数化すると、処理の流れを追うために多数の関数を移動することになります。独立した役割、再利用、検証の単位を基準に分割します。

練習問題

流量を単位変換する

L/minをL/sへ変換して返す関数を作ってください。

解答例です。

def convert_l_min_to_l_s(flow_l_min: float) -> float:
    return flow_l_min / 60


flow_l_s = convert_l_min_to_l_s(120.0)
print(flow_l_s)

実行結果です。

2.0

確認する点は、関数が表示ではなく計算結果を返していることです。

圧力の範囲を判定する

圧力が最小値以上、最大値以下ならTrueを返す関数を作ってください。標準の範囲は0 MPa以上16 MPa以下とします。

解答例です。

def is_pressure_in_range(
    pressure_mpa: float,
    minimum_mpa: float = 0.0,
    maximum_mpa: float = 16.0,
) -> bool:
    return minimum_mpa <= pressure_mpa <= maximum_mpa


print(is_pressure_in_range(14.0))
print(is_pressure_in_range(18.0))

実行結果です。

True
False

デフォルト引数を使いながら、必要なら範囲を変更できます。

is_pressure_in_range(
    pressure_mpa=18.0,
    minimum_mpa=10.0,
    maximum_mpa=21.0,
)

計算と表示を分ける

長さをmmからmへ変換する関数と、表示文字列を作る関数を別々に作ってください。

解答例です。

def convert_mm_to_m(length_mm: float) -> float:
    return length_mm / 1000


def format_length(length_m: float) -> str:
    return f"Length: {length_m:.3f} m"


length_m = convert_mm_to_m(1250.0)
message = format_length(length_m)
print(message)

期待される出力です。

Length: 1.250 m

計算結果を表示以外にも再利用できることが確認点です。

まとめ

関数を使うと、処理へ名前を付け、入出力を明確にできます。

  • defで関数を定義する
  • 引数として処理に必要な値を渡す
  • returnで計算結果を返す
  • print()とreturnは役割が異なる
  • デフォルト引数で標準値を設定できる
  • キーワード引数で値の意味を明確にできる
  • 変更可能なオブジェクトをデフォルト値へ直接置かない
  • ローカル変数は関数内部で使用する
  • グローバルな状態の変更を減らす
  • docstringで目的、単位、前提を説明する
  • 型ヒントは想定する型を示すが、実行時の検証を代替しない
  • 入力・計算・出力を分離すると再利用とテストがしやすくなる

関数化の目的は、単にコードを短くすることではありません。計算ロジックの入出力を定義し、同じ処理を安全に再利用できる部品へ変えることです。

参考情報

参考になったらシェアしてください
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

機械設計・油圧・CAD・Python・AIなど、ものづくりに関わる技術を扱っています。工学知識を整理・構造化し、設計や自動化に再利用できる形へ変えていくことを目指しています。

目次