関数とモジュール:コードを整理して再利用する方法

関数とモジュール:コードを整理して再利用する方法

プログラミングを続けていると、同じような処理を何度も書いていることに気づくはずです。そんな時に重要になるのが関数モジュールの概念です。

今回は、Pythonで効率的なコードを書くための関数とモジュールの使い方を、実例を交えながら詳しく解説します。

関数の基本:なぜ関数が必要なのか?

関数を使わない場合の問題点

まず、関数を使わずに書いたコードを見てみましょう:

# 税込み価格の計算を3回行う例
price1 = 1000
tax_included_price1 = price1 * 1.1
print(f"商品1の税込み価格: {tax_included_price1}円")

price2 = 2500
tax_included_price2 = price2 * 1.1
print(f"商品2の税込み価格: {tax_included_price2}円")

price3 = 800
tax_included_price3 = price3 * 1.1
print(f"商品3の税込み価格: {tax_included_price3}円")

このコードには以下の問題があります:

  • 同じ計算式(価格 * 1.1)を何度も書いている
  • 税率が変わった時、すべての箇所を修正する必要がある
  • コードが冗長で読みにくい

関数を使った改善版

同じ処理を関数として定義してみましょう:

def calculate_tax_included_price(price, tax_rate=0.1):
    """税込み価格を計算する関数

    Args:
        price (float): 税抜き価格
        tax_rate (float): 税率(デフォルト: 0.1 = 10%)

    Returns:
        float: 税込み価格
    """
    return price * (1 + tax_rate)

# 関数を使って計算
prices = [1000, 2500, 800]
for i, price in enumerate(prices, 1):
    tax_included = calculate_tax_included_price(price)
    print(f"商品{i}の税込み価格: {tax_included}円")

改善点:

  • 計算ロジックが1箇所にまとまった
  • 税率変更時の修正が1箇所だけで済む
  • コードが簡潔で読みやすい
  • 他の場所でも簡単に再利用できる

関数の定義と使い方

基本的な関数の定義

def 関数名(引数1, 引数2, ...):
    """関数の説明(docstring)"""
    # 処理内容
    return 戻り値  # 必要に応じて

実用的な関数の例

1. 引数なし・戻り値なしの関数

def greet():
    """挨拶を表示する関数"""
    print("こんにちは!プログラミングの世界へようこそ!")

# 関数の呼び出し
greet()

2. 引数あり・戻り値ありの関数

def calculate_bmi(weight, height):
    """BMIを計算する関数

    Args:
        weight (float): 体重(kg)
        height (float): 身長(m)

    Returns:
        float: BMI値
    """
    bmi = weight / (height ** 2)
    return round(bmi, 2)

# 使用例
my_bmi = calculate_bmi(70, 1.75)
print(f"あなたのBMIは {my_bmi} です")

3. デフォルト引数を持つ関数

def create_user_profile(name, age, city="東京"):
    """ユーザープロフィールを作成する関数

    Args:
        name (str): 名前
        age (int): 年齢
        city (str): 住所(デフォルト: "東京")

    Returns:
        dict: ユーザープロフィール辞書
    """
    profile = {
        "name": name,
        "age": age,
        "city": city
    }
    return profile

# 使用例
user1 = create_user_profile("田中太郎", 35)
user2 = create_user_profile("鈴木花子", 28, "大阪")

print(user1)  # {'name': '田中太郎', 'age': 35, 'city': '東京'}
print(user2)  # {'name': '鈴木花子', 'age': 28, 'city': '大阪'}

4. 可変長引数を持つ関数

def calculate_average(*numbers):
    """数値の平均を計算する関数(引数の個数は任意)

    Args:
        *numbers: 任意の個数の数値

    Returns:
        float: 平均値
    """
    if not numbers:
        return 0

    total = sum(numbers)
    count = len(numbers)
    return total / count

# 使用例
avg1 = calculate_average(10, 20, 30)
avg2 = calculate_average(5, 15, 25, 35, 45)

print(f"平均1: {avg1}")  # 平均1: 20.0
print(f"平均2: {avg2}")  # 平均2: 25.0

モジュールの基本:コードをファイルに分けて管理

関数が増えてくると、今度はファイルが長くなりすぎる問題が発生します。この問題を解決するのがモジュールです。

モジュールの作成

例として、数学計算に関する関数をまとめたモジュールを作成してみましょう。

math_utils.py

"""数学計算のユーティリティ関数をまとめたモジュール"""

import math

def calculate_circle_area(radius):
    """円の面積を計算する関数

    Args:
        radius (float): 半径

    Returns:
        float: 円の面積
    """
    return math.pi * radius ** 2

def calculate_rectangle_area(width, height):
    """長方形の面積を計算する関数

    Args:
        width (float): 幅
        height (float): 高さ

    Returns:
        float: 長方形の面積
    """
    return width * height

def fahrenheit_to_celsius(fahrenheit):
    """華氏温度を摂氏温度に変換する関数

    Args:
        fahrenheit (float): 華氏温度

    Returns:
        float: 摂氏温度
    """
    return (fahrenheit - 32) * 5 / 9

def celsius_to_fahrenheit(celsius):
    """摂氏温度を華氏温度に変換する関数

    Args:
        celsius (float): 摂氏温度

    Returns:
        float: 華氏温度
    """
    return celsius * 9 / 5 + 32

# モジュール内で定数を定義することも可能
PI = 3.14159
GOLDEN_RATIO = 1.618

モジュールの使用方法

1. モジュール全体をインポート

import math_utils

# モジュール名.関数名で呼び出し
circle_area = math_utils.calculate_circle_area(5)
temp_celsius = math_utils.fahrenheit_to_celsius(100)

print(f"半径5の円の面積: {circle_area}")
print(f"華氏100度は摂氏{temp_celsius}度")

2. 特定の関数のみをインポート

from math_utils import calculate_circle_area, fahrenheit_to_celsius

# 直接関数名で呼び出し可能
circle_area = calculate_circle_area(5)
temp_celsius = fahrenheit_to_celsius(100)

print(f"半径5の円の面積: {circle_area}")
print(f"華氏100度は摂氏{temp_celsius}度")

3. エイリアス(別名)を使ったインポート

import math_utils as math_util

# 短縮名で呼び出し
area = math_util.calculate_rectangle_area(10, 20)
print(f"10×20の長方形の面積: {area}")

実践例:仮想通貨価格計算モジュール

実際のプロジェクトでよく使いそうな、仮想通貨関連の計算をまとめたモジュールを作成してみましょう。

crypto_utils.py

"""仮想通貨関連の計算ユーティリティモジュール"""

def calculate_profit_loss(buy_price, sell_price, amount):
    """売買の損益を計算する関数

    Args:
        buy_price (float): 購入価格
        sell_price (float): 売却価格
        amount (float): 取引量

    Returns:
        dict: 損益情報の辞書
    """
    profit_loss = (sell_price - buy_price) * amount
    profit_loss_rate = (sell_price - buy_price) / buy_price * 100

    return {
        "profit_loss": round(profit_loss, 2),
        "profit_loss_rate": round(profit_loss_rate, 2),
        "is_profit": profit_loss > 0
    }

def calculate_portfolio_value(holdings):
    """ポートフォリオの総額を計算する関数

    Args:
        holdings (list): [{"symbol": "BTC", "amount": 0.1, "current_price": 5000000}, ...]

    Returns:
        dict: ポートフォリオ情報
    """
    total_value = 0
    details = []

    for holding in holdings:
        symbol = holding["symbol"]
        amount = holding["amount"]
        current_price = holding["current_price"]
        value = amount * current_price

        total_value += value
        details.append({
            "symbol": symbol,
            "amount": amount,
            "current_price": current_price,
            "value": round(value, 2)
        })

    return {
        "total_value": round(total_value, 2),
        "details": details
    }

def calculate_dca_average(purchases):
    """ドルコスト平均法の平均取得価格を計算する関数

    Args:
        purchases (list): [{"price": 5000000, "amount": 0.01}, ...]

    Returns:
        dict: DCA情報
    """
    total_cost = sum(p["price"] * p["amount"] for p in purchases)
    total_amount = sum(p["amount"] for p in purchases)

    if total_amount == 0:
        return {"average_price": 0, "total_cost": 0, "total_amount": 0}

    average_price = total_cost / total_amount

    return {
        "average_price": round(average_price, 2),
        "total_cost": round(total_cost, 2),
        "total_amount": total_amount
    }

使用例: main.py

from crypto_utils import calculate_profit_loss, calculate_portfolio_value, calculate_dca_average

# 損益計算の例
trade_result = calculate_profit_loss(
    buy_price=4500000,  # 450万円で購入
    sell_price=5000000,  # 500万円で売却
    amount=0.1  # 0.1BTC
)

print("=== 取引結果 ===")
print(f"損益: {trade_result['profit_loss']}円")
print(f"損益率: {trade_result['profit_loss_rate']}%")
print(f"利益?: {'はい' if trade_result['is_profit'] else 'いいえ'}")

# ポートフォリオ計算の例
my_portfolio = [
    {"symbol": "BTC", "amount": 0.05, "current_price": 5000000},
    {"symbol": "ETH", "amount": 2.0, "current_price": 400000},
    {"symbol": "XRP", "amount": 1000, "current_price": 100}
]

portfolio_info = calculate_portfolio_value(my_portfolio)

print("n=== ポートフォリオ ===")
print(f"総額: {portfolio_info['total_value']}円")
for detail in portfolio_info['details']:
    print(f"{detail['symbol']}: {detail['amount']} x {detail['current_price']}円 = {detail['value']}円")

# DCA計算の例
my_purchases = [
    {"price": 4000000, "amount": 0.01},
    {"price": 4500000, "amount": 0.01},
    {"price": 5000000, "amount": 0.01}
]

dca_info = calculate_dca_average(my_purchases)

print("n=== ドルコスト平均法 ===")
print(f"平均取得価格: {dca_info['average_price']}円")
print(f"総投資額: {dca_info['total_cost']}円")
print(f"総保有量: {dca_info['total_amount']}BTC")

パッケージ:モジュールをさらに整理する

複数のモジュールを関連するテーマごとにまとめたい場合は、パッケージを使用します。

パッケージの構造例

crypto_tools/
├── __init__.py          # パッケージであることを示すファイル
├── calculations.py      # 計算関連のモジュール
├── data_fetcher.py     # データ取得関連のモジュール
└── analysis.py         # 分析関連のモジュール

crypto_tools/init.py

"""仮想通貨分析ツールパッケージ"""

# パッケージの初期化時にインポートしたい関数を指定
from .calculations import calculate_profit_loss, calculate_portfolio_value
from .data_fetcher import get_crypto_price
from .analysis import calculate_rsi

# パッケージのバージョン情報
__version__ = "1.0.0"
__author__ = "Your Name"

使用例

# パッケージ全体をインポート
import crypto_tools

# またはパッケージから特定の関数をインポート
from crypto_tools import calculate_profit_loss, get_crypto_price

良い関数・モジュール設計のポイント

1. 単一責任の原則

各関数は1つのことだけを行うように設計しましょう。

# 良い例:1つの機能に集中
def calculate_tax(amount, tax_rate):
    return amount * tax_rate

def format_currency(amount):
    return f"{amount:,}円"

# 悪い例:複数の責任を持つ
def calculate_and_format_tax(amount, tax_rate):
    tax = amount * tax_rate
    return f"{tax:,}円"  # 計算と表示の責任が混在

2. 適切な関数名

関数名から何をする関数かが分かるように命名しましょう。

# 良い例
def calculate_monthly_payment(principal, rate, months):
    pass

def validate_email_format(email):
    pass

# 悪い例
def calc(p, r, m):  # 何を計算するのか不明
    pass

def check(data):  # 何をチェックするのか不明
    pass

3. docstringの活用

関数の説明、引数、戻り値を明確に記述しましょう。

def compound_interest(principal, rate, time, compound_frequency=1):
    """複利計算を行う関数

    Args:
        principal (float): 元本
        rate (float): 年利率(0.05 = 5%)
        time (int): 運用期間(年)
        compound_frequency (int): 年間の複利計算回数(デフォルト: 1)

    Returns:
        dict: 最終金額と利益を含む辞書

    Example:
        >>> result = compound_interest(1000000, 0.05, 10)
        >>> print(result['final_amount'])
        1628894.63
    """
    final_amount = principal * (1 + rate / compound_frequency) ** (compound_frequency * time)
    profit = final_amount - principal

    return {
        'final_amount': round(final_amount, 2),
        'profit': round(profit, 2)
    }

まとめ

関数とモジュールを適切に使うことで:

  • コードの再利用性が向上する
  • 保守性が高くなる(修正が容易)
  • 可読性が向上する(理解しやすい)
  • テストが容易になる
  • チーム開発が効率的になる

最初は小さな関数から始めて、徐々に複雑な処理をモジュール化していくことをお勧めします。プログラミングの上達につながる重要な概念なので、ぜひ実際のプロジェクトで活用してみてください。

関連記事