Pythonのdefによる関数の定義と使い方、引数や戻り値をわかりやすく解説

スポンサーリンク
スポンサーリンク
amazon
スマイルSALE
--:--:--
ad. 価格範囲を指定して商品を探せます

defの概要

関数の定義と再利用可能な処理の作成 Python予約語

def クラス名:

概要 def(デフ)は、Pythonにおいて「関数(独自の処理のまとまり)」を定義するための最重要キーワードです。英語の「define(定義する)」が由来であり、一連のコードに固有の名前をつけてパーツ化(モジュール化)することで、いつでもどこからでも同じ処理を再利用できるようにします。

わかりやすく説明 def を使うと、プログラムの中に「特製の全自動調理ロボット」を自作して配置することができます。「材料(引数)」をロボットに投入してスタートボタン(関数呼び出し)を押すと、内部の「あらかじめ決まった手順(処理)」が実行され、最後に「完成した料理(戻り値)」が手元に出てくる、という一連の仕組みを自由にデザインできます。

  • def 関数名(): の形式で宣言し、コロン(:)の後に続くインデントブロック内に関数の本体を記述します。
  • 関数は「引数(ひきすう)」を受け取ることができ、呼び出し側の都合に合わせて内部の動きを動的にコントロールできます。
  • 計算や加工の成果は「戻り値(返り値)」として return で外へ放り出し、別の変数や次の処理へバトンタッチできます。

defの考え方をイメージで理解

defの基本的な使い方

引数も戻り値も持たない、最もシンプルな「特定の定型文を画面に表示するだけ」の関数を定義して実行する例です。

# 基本的な関数の定義
def greet():
    print("こんにちは!")

# 関数の呼び出し(実行)
greet()  # 結果: こんにちは!
greet()  # 一度作れば、名前を呼ぶだけで何度でも再利用できます
  • def を記述して定義した段階では、ロボットが工場に設置されただけなので何も動きません。末尾のように greet() とカッコをつけて名前を呼ぶことで初めて中身のコードが起動します。

引数(ひきすう)を持つ関数

外部からデータを受け取るための窓口(変数)をカッコ内に用意することで、呼び出すたびに出力結果を柔軟に変えられる関数になります。

# 引数「name」を窓口として受け取る関数
def greet_user(name):
    print(f"こんにちは、{name}さん!")

# 引数に具体的なデータを流し込んで呼び出す
greet_user("太郎")  # 結果: こんにちは、太郎さん!
greet_user("花子")  # 結果: こんにちは、花子さん!
  • 関数側で仮に置いた変数を「仮引数」、呼び出し側が実際に渡した "太郎" などの生データを「実引数」と呼びます。

戻り値(返り値)を持つ関数

関数の中で計算・加工した成果物を、呼び出し元のコードへ「成果データ」として持ち帰らせる仕組みが return です。

# 戻り値を持つ関数
def calculate_tax(price):
    return int(price * 1.1)  # 消費税込みの金額を外に投げ返す

# 返ってきた成果物を変数「total_price」でキャッチする
total_price = calculate_tax(1000)
print(total_price)  # 結果: 1100(この値を使ってさらに別の計算が可能です)

デフォルト引数(初期値設定)を持つ関数

引数の後ろに = 値 をつけておくことで、呼び出し側が引数を省略した際に自動適用される安全な「初期値」を設定できます。

# 引数が省略されたら自動的に「ゲスト」にする
def show_welcome(name="ゲスト"):
    print(f"ようこそ、{name}さん!")

show_welcome()        # 引数なし ⇒ 結果: ようこそ、ゲストさん!
show_welcome("山田")  # 引数あり ⇒ 結果: ようこそ、山田さん! (デフォルト値が上書きされます)

可変長引数(*args, **kwargs)

「呼び出されるまで、引数が何個送られてくるか事前には予測できない」というシチュエーションを丸ごと回収する強力な記述法です。

# 複数のバラバラな引数をタプルとして一括回収する「*args」
def sum_all(*numbers):
    return sum(numbers)

print(sum_all(1, 2, 3))        # 結果: 6
print(sum_all(10, 20, 30, 40)) # 結果: 100

# 「引数名=値」のペアを辞書として一括回収する「**kwargs」
def print_profile(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_profile(name="佐藤", age=30, job="Engineer")

関数の中で関数を定義(ネストされた関数)

親となる大きな関数の中に、その内部だけで独占利用するミニ関数(インナー関数)を隠蔽してカプセル化する例です。

def main_process():
    print("メイン処理を開始します")
    
    # 内部限定の隠し関数
    def sub_helper():
        print("補助的な処理を実行しました")
        
    sub_helper()  # 内部でのみ呼び出し可能

main_process()
# sub_helper() # ←【エラー】ここで直接呼ぶと外側からは見えないため NameError になります

関数の注意点

  • 関数内で作った変数(ローカル変数)を外から覗くことはできない(スコープの罠):関数の内部(インデントの中)で新しく作成した変数は、その関数が実行されている間だけメモリ上に現れ、関数が終わると同時に消滅します。この範囲をローカルスコープと呼び、関数の外から呼び出そうとするとプログラムが強制停止します。
    # 【クラッシュする失敗コード】
    def make_total():
        subtotal = 500  # 関数内だけのローカル変数
        return subtotal
    
    make_total()
    print(subtotal)  # エラー! NameError: name 'subtotal' is not defined (外からは存在を知り得ません)
    
    # 【変数の受け渡しを意識した正しい対策コード】
    received_value = make_total() # 戻り値として外に出たデータを別の変数に格納する
    print(received_value)         # 結果: 500
  • デフォルト引数にミュータブルな(変更可能な)データ型を設定してはいけない(実務での致命的なバグ):関数の初期値として空のリスト([])や空の辞書({})を指定すると、そのオブジェクトは関数が呼び出されるたびに新しく作られるのではなく、システム内で「1つの同じ箱」がずっと使い回されます。その結果、2回目以降の呼び出しで前のデータが勝手に残る怪奇現象(バグ)に襲われます。初期値は必ず None にし、関数内でリストを生成するのがプロの絶対的な常識です。
    # 【ゾンビのようにデータが蓄積する失敗コード】
    def add_item_bug(item, target_list=[]): # リストを初期値にするのは危険!
        target_list.append(item)
        return target_list
    
    print(add_item_bug("りんご")) # 結果: ['りんご']
    print(add_item_bug("バナナ")) # 結果: ['りんご', 'バナナ'] (空のリストから始まるはずが、前のデータが残っている!)
    
    # 【安全を確保した実務の対策コード】
    def add_item_safe(item, target_list=None):
        if target_list is None:
            target_list = [] # 呼び出された瞬間にまっさらなリストを関数内で生成する
        target_list.append(item)
        return target_list
    
    print(add_item_safe("りんご")) # 結果: ['りんご']
    print(add_item_safe("バナナ")) # 結果: ['バナナ'] (正常に独立しています)

defのよくある質問

Q: `print()` と `return` の違いがどうしてもごっちゃになります。初心者に分かりやすく教えてください。
A: 「テレビ画面に映して終わり(print)」か、「次の計算に使うためにデータをダンボールに詰めて外へ出荷する(return)」かの違いです。 print() は単に人間に結果を見せるための液晶画面への出力であり、コンピュータはその出力された文字を使って足し算や引き算などの再加工をすることは一切できません。実務において、関数で作ったデータを後続のプログラムに引き継ぎたい場合は、画面表示ではなく必ず return を用いてデータを「戻す」必要があります。

# 【データを次に活かせない失敗コード】
def get_price_print():
    print(1000) # 画面に1000と映るだけ(関数自体の戻り値は空っぽの None になる)

total = get_price_print() + 200 # エラー! None型 と 数値 は足し算できません(TypeError)

# 【データを次の処理へ引き継げる対策コード】
def get_price_return():
    return 1000

total = get_price_return() + 200 # 成功! 1000 + 200 が計算されて 1200 になります
Q: 関数をわざわざ小分けに作る「実務的な基準」はありますか?1つのファイルに上から下までズラーッと書いた方が楽に見えるのですが…
A: 主な基準は「同じ処理が2回以上登場したとき(重複排除)」「処理に名前をつけて分かりやすくしたいとき(可読性向上)」です。 上から下までズラーッと書かれたコードは、後から一部を修正したいときにどこを直せばいいか分からなくなり、関係ない場所まで巻き添えで壊してしまう危険(密結合)があります。たとえば「メールを送信する処理」を def send_email(): として1つのパーツに切り出しておけば、文面を変更したいときにその関数の内部だけを直せばよく、他の計算ロジックなどを傷つける心配がゼロになります。1回しか使わない処理であっても、20行を超えるようなまとまった塊であれば、関数化して名前をつけるのが綺麗な設計の鉄則です。

まとめ

def は、スパゲッティのように絡まりがちなソースコードを、機能ごとに綺麗に切り分けて統治するための「整理整頓の超重要キーワード」です。

  • def 関数名(): の宣言によって、再利用可能な独立した処理ブロックを自由にデザインする。
  • 引数を用いて処理に「インプット(入力)」を与え、return を用いて呼び出し元に「アウトプット(出力)」を戻す関係性を構築する。
  • 関数内の変数はローカルスコープに守られる性質を理解し、安全なデータのやり取りを設計する。

あらゆるシステム開発、AI・機械学習スクリプト、WebAPIの構築において、プログラムの最小単位はすべてこの def による関数の集合体でできています。引数と戻り値のメカニズムを完璧にコントロールし、美しくメンテナンス性の高いプロのコードへと昇華させましょう。