CHAPTER 4 ・ コードを整理して育てる / Lesson 13
使いやすい関数を設計する
初期値つきの引数、名前つきの引数、複数の戻り値、説明文(docstring)。他人にも自分にも優しい関数にします。
このレッスンが終わったら
「関数は、1つの役目だけにする。引数の初期値と名前で呼び出しを読みやすくし、docstring で使い方を残す」と言える。
こんな場面、ありませんか?
基礎編では、関数に「材料」を渡して、答えを返してもらう書き方を学びました。ところが、材料が増えてくると、呼び出す側が読めなくなることがあります。
売上の金額を出す関数を、次のように作ったとします。「単価・数量・税率(%)・税込にするか」の4つを受け取ります(// は、割り算の整数部分だけを出す記号で、円未満は切り捨てになります)。
▶ 実行を押すと、ここに結果が出ます
動きは正しいのですが、呼び出しの行だけを見て、10, True が何を表すか分かるでしょうか。ほかにも困りごとがあります。
- 税率はほとんど10%なのに、毎回
10と書く必要がある - 半年後に読み返すと、自分でも「この
Trueは何だっけ?」となる
この回では、関数を使う人(未来の自分を含む)にやさしく作る道具を学びます。
初期値と名前を付けてみる
さきほどの関数を、2か所だけ変えます。rate=10 と with_tax=False を、引数のところに書き足します。
▶ 実行を押すと、ここに結果が出ます
結果は先ほどと同じ(105000、115500、16200)です。税率が10%のときは書かなくてよく、変えたいときだけ rate=8 のように名前を付けて渡します。with_tax=True と読めば、意味も分かります。順番を入れ替えて amount(qty=3, with_tax=True, price=35000) と呼ぶこともできます。
仕組みを読む
def の行を、部品に分けて見ます。
price, qty必ず渡す材料(初期値なし)rate=10初期値つき。省略すると 10with_tax=False初期値つき。省略すると False=「省略したらこの値」の印amount(35000, 3)単価と数量だけ渡す。rate は 10、with_tax は False になるamount(35000, 3, with_tax=True)rate は省略(10)。with_tax だけ True にするamount(1500, 10, rate=8, with_tax=True)初期値と違う rate と with_tax を、名前を付けて渡す日本語にすると
変えて、試す
① 初期値つきの引数は、必ず後ろに置く。 前に置くと、Pythonに叱られます。
▶ 実行を押すと、ここに結果が出ます
SyntaxError(書き方の間違い)と出ます。「必ず渡すもの → 省略できるもの」の順に並べる決まりです。
② 答えを2つ返す。 税込の金額から、「税抜の金額」と「税額」を分けたいとします。return にカンマで2つ並べると、2つをひとまとまりにして返せます。受け取る側も、カンマで2つの名前を並べます。
▶ 実行を押すと、ここに結果が出ます
1つ目の (105000, 10500) は、2つの値が丸い括弧でひとまとめになったもの(タプルと呼びます)です。2つ目のように base, tax = ... と書くと、1つずつ名前を付けて取り出せます。1000円(税込)は、909 と 91 に分かれました。端数の扱いは、会社のルールに合わせます(Lesson 04 で学んだ通りです)。
③ 落とし穴:初期値に、空のリストを書かない。 「項目を足していくリスト」を、初期値 [] にしてみます。
▶ 実行を押すと、ここに結果が出ます
上の2行は、2回目が ['会議費'] ではなく ['交通費', '会議費'] になりました。初期値のリストは、関数を作ったときに1回だけ作られ、呼ぶたびに使い回されるためです。直し方は、初期値を None(「何もない」を表す値)にして、中で新しく作ること(下の2行)です。リストや辞書を初期値にしたくなったら、None にする、と覚えておきましょう。
④ 落とし穴:渡されたリストを、書き換えない。 関数が、渡されたリストの中身をこっそり書き換える(副作用と呼びます)と、呼び出した側のデータまで変わってしまいます。
▶ 実行を押すと、ここに結果が出ます
1行目は、original まで [1100, 2200] に変わっています。元のデータが消える事故につながります。2行目は、original が [1000, 2000] のままです。受け取ったものは書き換えず、新しく作って返すと、安全です。
自分で書く
お題① 宛名に敬称を付ける関数 add_suffix(name, suffix="様") を作ってください。名前と敬称をつなげて返します。敬称を省略すると「様」、suffix="御中" のように渡すと、その敬称になります。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
def add_suffix(name, suffix="様"): と書き、return name + suffix とします。文字は + でつなげます。
答えの例を見る
def add_suffix(name, suffix="様"):
return name + suffix
print(add_suffix("田中"))
print(add_suffix("佐藤", "さん"))
print(add_suffix("株式会社サクラ印刷", suffix="御中"))お題② 長いスクリプトは、「読む」と「集計する」を別の関数に分けると、使い回せます。
1本のスクリプト
rows = list(csv.DictReader(open("sales_detail.csv", encoding="utf-8")))
total = 0
count = 0
for r in rows:
if r["日付"].startswith("2025-09"):
total += int(r["数量"]) * int(r["単価"])
count += 1
print(total, count)関数に分けた形
rows = load_rows() total, count = month_summary(rows, "2025-09") print(total, count)
load_rows は用意してあります。指定した月の売上合計と、伝票の件数を返す関数 month_summary(rows, month) を作ってください。日付が month(例 "2025-09")で始まる行が対象で、売上は「数量 × 単価」です。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
日付が month で始まる行だけを選びます(r["日付"].startswith(month))。合計は、int(r["数量"]) * int(r["単価"]) を足し、件数は選んだ行の len です。最後に return 合計, 件数。
答えの例を見る
import csv
def load_rows(path="sales_detail.csv"):
"""売上明細CSVを読み、1行ずつの辞書のリストで返す。"""
with open(path, encoding="utf-8") as f:
return list(csv.DictReader(f))
def month_summary(rows, month):
"""month(例 2025-09)の売上合計と、伝票の件数を返す。"""
target = [r for r in rows if r["日付"].startswith(month)]
total = sum(int(r["数量"]) * int(r["単価"]) for r in target)
return total, len(target)
rows = load_rows()
total, count = month_summary(rows, "2025-09")
print(total, count)仕事ならどう使う?
理解チェックdef calc(price, qty=1, rate=10): と作った関数があります。calc(500, rate=8) を呼ぶと、qty はいくつになるでしょう?
理解チェックdef add(item, items=[]): という書き方が、おすすめされない理由は?