関数は処理に名前を付けて再利用するしくみです。本記事では関数の基本(引数・戻り値・デフォルト引数・キーワード引数)を整理し、関数に分ける理由を株価分析の例で説明します。
目次
- 関数の基本形
- なぜ関数に分けるのか
- 戻り値がない関数
- デフォルト引数 — 値を省略可能にする
- キーワード引数 — 名前で渡す
- 可変長引数 — *args と **kwargs
- 関数を組み合わせる
- 引数を変更しない設計
- docstring — 関数の説明を書く
関数の基本形
関数は def キーワードで定義します。
def average(values: list[float]) -> float: return sum(values) / len(values)
prices = [2900, 2925, 2880, 2910, 2945]print(average(prices)) # 2912.0構成要素は次のとおりです。
defの後ろに 関数名 を書く(スネークケースが慣例)- カッコの中が 引数 の宣言
->の後ろが 戻り値の型ヒント(省略可、#3-11「型ヒントの基本」 で詳述)- 関数の末尾は
returnで値を返す
なぜ関数に分けるのか
同じ処理を複数の場所で書くと、修正のときに必ず食い違いが起きます。関数にまとめる理由は次の 3 点です。
- 再利用: 同じ処理を一行で呼び出せる
- テスト容易性: 入力と出力の関係だけを確認できる
- 可読性: 名前の付いた処理として読めるため、本筋を追いやすい
戻り値がない関数
何かを表示するだけの関数など、戻り値がない場合もあります。return を省略すると None が返ります。
def print_summary(prices: list[float]) -> None: print(f"件数: {len(prices)}") print(f"平均: {sum(prices) / len(prices):.2f}")
print_summary([2900, 2925, 2880])デフォルト引数 — 値を省略可能にする
引数に既定値を持たせると、呼び出し側で省略できます。
def moving_average(values: list[float], window: int = 5) -> list[float]: if len(values) < window: return [] return [ sum(values[i - window + 1 : i + 1]) / window for i in range(window - 1, len(values)) ]
prices = [100, 102, 101, 103, 105, 107, 110]print(moving_average(prices)) # 既定 (5 日)print(moving_average(prices, 3)) # 3 日に変更「ほとんどの場合は 5 日窓だが、たまに 3 日窓にもしたい」というケースに向いています。
キーワード引数 — 名前で渡す
引数は順番だけでなく、名前=値 の形でも渡せます。
def fetch_prices(ticker: str, start: str, end: str) -> list[float]: # 実際は API 呼び出しなどを行う return []
# 位置引数fetch_prices("7203", "2026-04-01", "2026-04-30")
# キーワード引数(順番を変えても良い)fetch_prices(ticker="7203", end="2026-04-30", start="2026-04-01")引数が増えてくると、キーワード引数のほうが意図が明確になります。
可変長引数 — *args と **kwargs
引数の数が決まらないときは、*args(タプル)や **kwargs(辞書)で受け取ります。
def total(*values: float) -> float: return sum(values)
print(total(1, 2, 3)) # 6print(total(2900, 2925, 2880)) # 8705
def describe(**fields: str) -> None: for key, value in fields.items(): print(f"{key}: {value}")
describe(ticker="7203", name="トヨタ自動車", market="プライム")主にライブラリ側で使われる書き方ですが、自分で書くケースもあります。
関数を組み合わせる
小さな関数を組み合わせて大きな処理を作ると、見通しが良くなります。
def daily_returns(prices: list[float]) -> list[float]: return [ (curr - prev) / prev for prev, curr in zip(prices[:-1], prices[1:]) ]
def average(values: list[float]) -> float: return sum(values) / len(values)
def average_return(prices: list[float]) -> float: return average(daily_returns(prices))
prices = [100, 102, 101, 103, 105]print(f"{average_return(prices):.4f}") # 0.0123「日次リターンを計算する」「平均を取る」という 2 つの責務を分けたことで、それぞれを単独でテストできます。
引数を変更しない設計
関数の中でリストや辞書を書き換えると、呼び出し元の値も変わってしまいます。
def append_zero_bad(values: list[float]) -> list[float]: values.append(0) # 引数を直接変更 return values
original = [1, 2, 3]result = append_zero_bad(original)print(original) # [1, 2, 3, 0] — 元のリストも変わってしまう新しいリストを返す書き方に直します。
def append_zero_safe(values: list[float]) -> list[float]: return values + [0]
original = [1, 2, 3]result = append_zero_safe(original)print(original) # [1, 2, 3] — 元は無傷print(result) # [1, 2, 3, 0]「引数は読み取り専用に扱う」のを基本姿勢にすると、思わぬバグが減ります。
docstring — 関数の説明を書く
関数の冒頭に文字列を書くと、それが説明として扱われます。
def average(values: list[float]) -> float: """値のリストから単純平均を計算する。
引数: values: 数値のリスト。空リストは渡さない。
戻り値: 単純平均。 """ return sum(values) / len(values)
print(average.__doc__)生成AI に関数を読んでもらうとき、docstring があると意図が伝わりやすくなります。
生成AI へのプロンプト例
関数の設計を生成AI に相談する例です。
次の責務を持つ Python 関数を書いてください。
- 名前: calculate_volatility- 引数: prices (list[float]), annualize (bool, 既定 True)- 処理: 日次リターンの標準偏差を計算する。 annualize=True なら sqrt(252) を掛けて年率換算する。- 戻り値: float- 制約: Python 3.12 / 標準ライブラリのみ- docstring と簡単な使用例を含める責務・引数・戻り値・制約を分けて書くと、想定どおりの関数が返ります(#7-2「データ分析のためのプロンプト設計」)。
まとめ
- 関数は
defで定義し、returnで値を返す - デフォルト引数とキーワード引数で呼び出し側の書きやすさが変わる
- 1 つの関数は 1 つの責務に絞ると再利用とテストがしやすい
- 引数を直接書き換えない設計を基本にする