第1部 Python基礎編
第5章 関数
同じ処理を何度も書くのは卒業。処理に名前をつけて「部品」にする方法を学びます。部品化はコードの品質と安全性の基盤です。
🎯 この章で学ぶこと
defによる関数の定義と呼び出し- 引数(入力)と戻り値(出力)
- デフォルト引数とキーワード引数
- 変数のスコープ(有効範囲)
- ドキュメント文字列(docstring)
5.1 関数とは — 処理の部品化
関数とは「入力を受け取り、決まった処理をして、結果を返す」部品です。def(defineの略)で定義します。
def greet(name):
"""名前を受け取って挨拶文を返す"""
return f"こんにちは、{name} さん"
message = greet("tanaka") # 呼び出し
print(message) # こんにちは、tanaka さん
print(greet("suzuki")) # こんにちは、suzuki さん
nameは引数(ひきすう) — 関数への入力returnは戻り値 — 関数の出力。returnの時点で関数は終了する- 定義しただけでは動かない。呼び出して初めて実行される
5.2 なぜ関数にするのか
「パスワードが規則を満たすかチェックする」処理を例に考えます。もしこの判定ロジックをコピペで10か所に書いたら、規則が変わったとき10か所すべてを直す必要があり、1か所でも直し忘れたらそこが穴になります。
def is_valid_password(password):
"""パスワードが最低限の要件を満たすか判定する"""
if len(password) < 12:
return False
if password.lower() == password: # 大文字が1つもない
return False
if password.upper() == password: # 小文字が1つもない
return False
return True
print(is_valid_password("abc")) # False
print(is_valid_password("CorrectHorse2026")) # True
セキュリティ上重要な処理(入力検証、認可チェック、パスワード判定など)は、必ず1つの関数にまとめて全員がそれを使うのが鉄則です。同じチェックがあちこちに散らばっていると、修正漏れ・実装のばらつきが生まれ、そこが攻撃者の突破口になります。「Single Point of Truth(真実は1か所に)」と覚えてください。
5.3 複数の引数・デフォルト引数・キーワード引数
def lock_account(username, threshold=5, notify=True):
"""失敗回数がしきい値以上ならロック判定する(教材用の枠組み)"""
print(f"{username}: しきい値={threshold}, 通知={notify}")
lock_account("tanaka") # デフォルト値が使われる
lock_account("suzuki", 3) # 位置で渡す
lock_account("sato", notify=False) # 名前で渡す(キーワード引数)
lock_account("yamada", threshold=10, notify=False)
tanaka: しきい値=5, 通知=True
suzuki: しきい値=3, 通知=True
sato: しきい値=5, 通知=False
yamada: しきい値=10, 通知=False
lock_account("sato", 5, False) より lock_account("sato", threshold=5, notify=False) のほうが、後から読む人(3ヶ月後の自分を含む)に意図が伝わります。
5.4 戻り値を活用する
関数は値を返せるので、「判定する関数」と「判定結果を使う側」を分離できます。複数の値を返すこともできます(実体はタプルです)。
def analyze_log(lines):
"""ログの行リストから (総数, 失敗数) を返す"""
failed = 0
for line in lines:
if "FAILED" in line:
failed += 1 # failed = failed + 1 の短縮形
return len(lines), failed
logs = [
"LOGIN OK user=tanaka",
"LOGIN FAILED user=admin",
"LOGIN FAILED user=root",
]
total, failed = analyze_log(logs) # 2つの戻り値を同時に受け取る
print(f"総件数 {total} 件中、失敗 {failed} 件")
+= という書き方
failed += 1 は failed = failed + 1 と同じ意味の短縮形です。頻出なのでここで覚えてしまいましょう。-=、*= などもあります。
5.5 変数のスコープ — どこから見えるか
関数の中で作った変数は、その関数の中でしか使えません。これを「ローカル変数」と呼びます。
def check():
result = "OK" # ローカル変数
return result
check()
print(result) # NameError! 関数の外からは見えない
逆に、関数の外で定義した変数(グローバル変数)は関数の中から読めますが、安易に頼るとどこで値が変わったのか追えないコードになります。原則は「関数が必要とするものは引数で渡し、結果は戻り値で返す」です。
変数の見える範囲を最小にする設計は、セキュリティの「最小権限の原則」のコード版です。影響範囲が狭いほど、バグも脆弱性も入り込みにくく、見つけやすくなります。
5.6 docstring — 関数の説明書
関数の先頭に """...""" で書く説明を docstring と呼びます。help() で参照でき、チーム開発では事実上の必須マナーです。
def mask_email(email):
"""メールアドレスの@より前を伏せ字にして返す。
例: mask_email("tanaka@example.com") → "t*****@example.com"
"""
local, domain = email.split("@")
return local[0] + "*" * (len(local) - 1) + "@" + domain
print(mask_email("tanaka@example.com")) # t*****@example.com
この mask_email のような「個人情報を伏せる」処理は、ログを安全に扱うための実務頻出パターンです。
まとめ
- 関数は
def 名前(引数):で定義し、returnで値を返す - 繰り返し使う処理・重要な判定は関数に一元化する
- デフォルト引数とキーワード引数で柔軟かつ読みやすく
- 変数は狭いスコープで。入力は引数、出力は戻り値が原則
- docstringで「何をする関数か」を書き残す
練習問題
摂氏温度を華氏に変換する関数 to_fahrenheit(celsius) を書いてください(華氏 = 摂氏 × 9 ÷ 5 + 32)。to_fahrenheit(25) が 77.0 になれば正解です。
解答を見る
def to_fahrenheit(celsius):
"""摂氏を華氏に変換する"""
return celsius * 9 / 5 + 32
print(to_fahrenheit(25)) # 77.0
5.2の is_valid_password に「数字を1文字以上含むこと」という条件を追加してください。ヒント:文字列 s の各文字 c について c.isdigit() で数字かどうか判定できます。
解答を見る
def is_valid_password(password):
"""パスワードが要件(12文字以上・大小英字・数字)を満たすか判定する"""
if len(password) < 12:
return False
if password.lower() == password:
return False
if password.upper() == password:
return False
has_digit = False
for c in password:
if c.isdigit():
has_digit = True
if not has_digit:
return False
return True
print(is_valid_password("CorrectHorse")) # False(数字なし)
print(is_valid_password("CorrectHorse2026")) # True
慣れてきたら any(c.isdigit() for c in password) と1行でも書けます。
IPアドレスの文字列を受け取り、「プライベートIPかどうか」をおおまかに判定する関数 is_private_ip(ip) を書いてください。10.、192.168.、172.16. のいずれかで始まればプライベートとみなして構いません。ヒント:startswith()
解答を見る
def is_private_ip(ip):
"""プライベートIPアドレスらしいかを簡易判定する(教材用)"""
return (ip.startswith("10.")
or ip.startswith("192.168.")
or ip.startswith("172.16."))
print(is_private_ip("192.168.1.5")) # True
print(is_private_ip("8.8.8.8")) # False
実務では標準ライブラリ ipaddress モジュールの ip_address(ip).is_private を使うとより正確です(第7章参照)。「すでにある安全な部品を使う」のもセキュアコーディングの基本です。