8
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[入門] Pythonで学ぶ、より良いコードを書くための実践的テクニック 第2回 制御フローと式の最適化 (簡潔なコメント/制御フローの改善/式の分割/変数の効果的な使用)

Last updated at Posted at 2025-06-15

image.png

第1回では、コードの可読性の基本概念、適切な命名規則、コードの美しさについて学びました。第2回では、より実践的なテクニックとして、効果的なコメントの書き方、制御フロー1の改善、複雑な式の分割方法、そして変数の適切な使用方法について解説します。

これらのテクニックを習得することで、コードの論理構造がより明確になり、デバッグ2や保守作業が格段に効率化されます。

コメントは正確で簡潔に

コメントを簡潔にしておく

コメントは限られたスペースで最大限の情報を伝える必要があります。冗長なコメント3は読まれない可能性が高く、メンテナンスの負担にもなります。

# === 悪い例:冗長すぎるコメント ===
def trim(text):
    # この関数は、引数として渡された文字列の
    # 先頭と末尾にある空白文字(スペース、タブ、改行など)を
    # すべて取り除いて、きれいな文字列を返します
    return text.strip()

# === 良い例:簡潔で要点を伝える ===
def trim(text):
    """前後の空白を除去"""
    return text.strip()

関数の動作を正確に記述する

関数の入出力の具体例を示すことで、動作を正確に説明できます。

def reverse(text):
    """文字列を反転する。例: reverse("abc")は"cba"を返す"""
    return text[::-1]

def format_percentage(value):
    """
    小数を百分率表記に変換する
    
    例:
        format_percentage(0.25) -> "25%"
        format_percentage(0.1234) -> "12.34%"
        format_percentage(1.5) -> "150%"
    """
    return f"{value * 100:.2f}%"

名前付き引数コメント

引数の意味が不明確な関数呼び出しでは、インラインコメント4を使って引数の名前を明示します。

# === 引数の意味が不明確 ===
connect(10, False)

# === インラインコメントで明示 ===
connect(10, False)  # timeout_secs=10, use_encryption=False

# === より良い方法:名前付き引数を使用 ===
connect(timeout_secs=10, use_encryption=False)

情報の濃密な言葉を使う

プログラミングの専門用語5を適切に使用することで、簡潔かつ正確な説明が可能になります。

class LRUCache:
    """
    LRU(Least Recently Used)キャッシュの実装
    
    キャッシュがいっぱいになったら、最も長く使われていない
    要素を削除する
    """
    pass

class DatabaseConnectionPool:
    """
    データベースコネクションプールを管理
    
    コネクションの再利用により、接続のオーバーヘッドを削減
    """
    pass

制御フローを読みやすくする

条件式の引数の並び順

比較では、変化する値を左に、定数を右に置くのが自然です。

# === 悪い例:定数を左に置く(不自然) ===
if 10 <= user_age:
    print("利用可能です")

if None == result:
    print("結果がありません")

# === 良い例:変化する値を左、定数を右に ===
if user_age >= 10:
    print("利用可能です")

if result is None:
    print("結果がありません")

if/elseブロックの並び順

肯定的な条件、単純な条件、重要な条件を先に置きます。

# === 悪い例:否定的な条件が先 ===
if not file_exists:
    print("ファイルが見つかりません")
    return None
else:
    with open(filename) as f:
        return f.read()

# === 良い例:肯定的な条件が先 ===
if file_exists:
    with open(filename) as f:
        return f.read()
else:
    print("ファイルが見つかりません")
    return None

関数から早く返す(早期リターン)

エラーケース6や特別な条件では早期リターンを使用して、ネストを浅く保ちます。

# === 悪い例:深いネスト ===
def calculate_payment(user, amount):
    if user is not None:
        if user.is_active:
            if amount > 0:
                if user.balance >= amount:
                    return process_payment(user, amount)
                else:
                    return "残高不足"
            else:
                return "無効な金額"
        else:
            return "アカウントが無効"
    else:
        return "ユーザーが見つかりません"

# === 良い例:早期リターンでネストを浅く ===
def calculate_payment(user, amount):
    if user is None:
        return "ユーザーが見つかりません"
    if not user.is_active:
        return "アカウントが無効"
    if amount <= 0:
        return "無効な金額"
    if user.balance < amount:
        return "残高不足"
    
    # すべての検証をパスした場合のみ支払い処理
    return process_payment(user, amount)

三項演算子の適切な使用

三項演算子7は簡単な条件でのみ使用し、複雑な条件では通常のif文を使います。

# === 良い例:単純な条件での使用 ===
status = "active" if user.is_active else "inactive"
display_name = user.nickname if user.nickname else user.full_name

# === 悪い例:複雑な条件での使用 ===
result = (calculate_complex_value() if check_multiple_conditions() 
          and validate_user_input() else get_default_value())

# === 改善例:if文を使用 ===
if check_multiple_conditions() and validate_user_input():
    result = calculate_complex_value()
else:
    result = get_default_value()

巨大な式を分割する

説明変数の導入

複雑な条件式は、説明変数8を使って意味を明確にします。

# === 悪い例:複雑な条件式 ===
if user.creation_date > datetime.now() - timedelta(days=30) and \
   user.purchase_count > 5 and \
   user.total_spent > 10000:
    apply_special_discount()

# === 良い例:説明変数で条件を分解 ===
is_new_user = user.creation_date > datetime.now() - timedelta(days=30)
is_frequent_buyer = user.purchase_count > 5
is_high_value_customer = user.total_spent > 10000

if is_new_user and is_frequent_buyer and is_high_value_customer:
    apply_special_discount()

要約変数の使用

複雑な処理を要約変数9に代入することで、コードの意図が明確になります。

# === 複雑な計算を直接使用 ===
if math.sqrt((x1 - x2)**2 + (y1 - y2)**2) < threshold:
    print("距離が閾値未満です")

# === 要約変数を使用 ===
distance = math.sqrt((x1 - x2)**2 + (y1 - y2)**2)
if distance < threshold:
    print("距離が閾値未満です")

ド・モルガンの法則の活用

論理否定の分配により、より読みやすい式に変換できます。

# === 悪い例:二重否定で読みにくい ===
if not (is_valid and is_authorized):
    return "アクセス拒否"

# === 良い例:ド・モルガンの法則を適用 ===
if not is_valid or not is_authorized:
    return "アクセス拒否"

# === さらに良い例:肯定的な変数名 ===
if is_invalid or is_unauthorized:
    return "アクセス拒否"

短絡評価の適切な使用

短絡評価10は便利ですが、過度に使用すると読みづらくなります。

# === 悪い例:短絡評価の悪用 ===
value = obj and obj.property and obj.property.subproperty or default

# === 良い例:明確な条件分岐 ===
if obj and obj.property and obj.property.subproperty:
    value = obj.property.subproperty
else:
    value = default

# === Python的な解決策:getattr使用 ===
value = getattr(getattr(obj, 'property', None), 'subproperty', default)

変数と読みやすさ

不要な変数を削除する

一度しか使われない中間変数11は、多くの場合削除できます。

# === 悪い例:不要な中間変数 ===
def calculate_total(items):
    temp_sum = 0
    for item in items:
        temp_sum += item.price
    total = temp_sum
    return total

# === 良い例:不要な変数を削除 ===
def calculate_total(items):
    return sum(item.price for item in items)

変数のスコープを縮める

変数のスコープ12は可能な限り狭くすることで、コードの理解が容易になります。

# === 悪い例:グローバル変数の使用 ===
total = 0  # グローバル変数

def process_orders(orders):
    global total
    for order in orders:
        total += order.amount
    print(f"合計: {total}")

# === 良い例:ローカル変数に限定 ===
def process_orders(orders):
    order_total = sum(order.amount for order in orders)
    print(f"合計: {order_total}")
    return order_total

変数は一度だけ書き込む

変数の値が頻繁に変化すると、コードの追跡が困難になります。

# === 悪い例:変数を何度も更新 ===
def process_text(text):
    result = text
    result = result.strip()
    result = result.lower()
    result = result.replace(' ', '_')
    return result

# === 良い例:メソッドチェーンを使用 ===
def process_text(text):
    return text.strip().lower().replace(' ', '_')

# === 別の良い例:中間結果に意味のある名前を付ける ===
def process_text(text):
    trimmed = text.strip()
    normalized = trimmed.lower()
    slug = normalized.replace(' ', '_')
    return slug

変数の利用を最小限にする

変数を過度に使用すると、かえって可読性が低下することがあります。

# === 過度な変数使用 ===
def is_valid_email(email):
    at_sign_count = email.count('@')
    has_one_at_sign = at_sign_count == 1
    
    dot_position = email.rfind('.')
    at_position = email.find('@')
    dot_after_at = dot_position > at_position
    
    return has_one_at_sign and dot_after_at

# === シンプルな実装 ===
def is_valid_email(email):
    return email.count('@') == 1 and email.rfind('.') > email.find('@')

実践的な例:リファクタリング

ここまで学んだテクニックを使って、実際のコードをリファクタリング13してみましょう。

Before: 読みにくいコード

def process_user_data(users, d):
    r = []
    for u in users:
        if u['age'] >= 18 and u['status'] == 'active' and \
           (u['subscription'] == 'premium' or u['subscription'] == 'gold'):
            temp = u['name'] + ' (' + str(u['age']) + ')'
            if u['country'] == 'JP':
                temp += ' [日本]'
            elif u['country'] == 'US':
                temp += ' [アメリカ]'
            else:
                temp += ' [その他]'
            r.append(temp)
    return r

After: リファクタリング後

def process_user_data(users, target_date):
    """
    成人かつアクティブなプレミアムユーザーの情報を整形する
    
    Args:
        users: ユーザー情報のリスト
        target_date: 処理対象日(現在は未使用)
    
    Returns:
        整形されたユーザー情報のリスト
    """
    ADULT_AGE = 18
    PREMIUM_SUBSCRIPTIONS = {'premium', 'gold'}
    COUNTRY_NAMES = {
        'JP': '日本',
        'US': 'アメリカ'
    }
    
    processed_users = []
    
    for user in users:
        # 条件チェック
        is_adult = user['age'] >= ADULT_AGE
        is_active = user['status'] == 'active'
        has_premium = user['subscription'] in PREMIUM_SUBSCRIPTIONS
        
        if not (is_adult and is_active and has_premium):
            continue
        
        # ユーザー情報の整形
        age_info = f"{user['name']} ({user['age']})"
        country_name = COUNTRY_NAMES.get(user['country'], 'その他')
        formatted_info = f"{age_info} [{country_name}]"
        
        processed_users.append(formatted_info)
    
    return processed_users

まとめ

第2回では、コードの制御フローと式の最適化に関する4つの重要な概念を解説しました:

  1. コメントは正確で簡潔に - 限られたスペースで最大限の情報を伝える
  2. 制御フローを読みやすくする - 条件式の順序と早期リターンの活用
  3. 巨大な式を分割する - 説明変数と要約変数で複雑さを管理
  4. 変数と読みやすさ - スコープの最小化と不要な変数の削除

これらのテクニックを組み合わせることで、複雑なロジックも明確で保守しやすいコードに変換できます。コードレビュー14の際にも、これらの観点から改善点を見つけることができるでしょう。

参考文献

  • Dustin Boswell、Trevor Foucher著、角征典訳『リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック』オライリー・ジャパン、2012年
  • Robert C. Martin著、花井志生訳『Clean Code アジャイルソフトウェア達人の技』アスキー・メディアワークス、2009年
  • Martin Fowler著、児玉公信、友野晶夫、平澤章、梅澤真史訳『リファクタリング 既存のコードを安全に改善する』オーム社、2014年
  1. 制御フロー - プログラムの実行順序を決定する構造。if文、for文、while文などの条件分岐や繰り返し処理を指す。

  2. デバッグ - プログラムの不具合(バグ)を見つけて修正する作業。効率的なデバッグには読みやすいコードが不可欠。

  3. 冗長なコメント - 必要以上に長く、コードから明らかなことを繰り返すコメント。保守の負担になりやすい。

  4. インラインコメント - コードの行末に記述する短いコメント。補足説明に使用されるが、多用は避けるべき。

  5. 専門用語 - プログラミング分野で広く認知されている技術用語。適切に使用することで簡潔な説明が可能になる。

  6. エラーケース - 正常でない状態や例外的な状況。早期リターンで処理することでメインロジックが明確になる。

  7. 三項演算子 - 条件 ? 真の場合 : 偽の場合 の形式で記述する条件式。Pythonでは 真の値 if 条件 else 偽の値 と記述する。

  8. 説明変数 - 複雑な条件式の一部を格納し、その意味を明確にするための変数。

  9. 要約変数 - 複雑な計算結果を保存し、再利用やデバッグを容易にするための変数。

  10. 短絡評価 - 論理演算において、結果が確定した時点で残りの評価を行わない最適化手法。andやorで使用される。

  11. 中間変数 - 計算の途中結果を一時的に保存する変数。必要性を検討し、不要なものは削除すべき。

  12. スコープ - 変数が参照可能な範囲。グローバルスコープ、関数スコープ、ブロックスコープなどがある。

  13. リファクタリング - 外部から見た動作を変えずに、コードの内部構造を改善する作業。

  14. コードレビュー - 他の開発者がコードを確認し、品質向上のための指摘や提案を行うプロセス。

8
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?