第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つの重要な概念を解説しました:
- コメントは正確で簡潔に - 限られたスペースで最大限の情報を伝える
- 制御フローを読みやすくする - 条件式の順序と早期リターンの活用
- 巨大な式を分割する - 説明変数と要約変数で複雑さを管理
- 変数と読みやすさ - スコープの最小化と不要な変数の削除
これらのテクニックを組み合わせることで、複雑なロジックも明確で保守しやすいコードに変換できます。コードレビュー14の際にも、これらの観点から改善点を見つけることができるでしょう。
参考文献
- Dustin Boswell、Trevor Foucher著、角征典訳『リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック』オライリー・ジャパン、2012年
- Robert C. Martin著、花井志生訳『Clean Code アジャイルソフトウェア達人の技』アスキー・メディアワークス、2009年
- Martin Fowler著、児玉公信、友野晶夫、平澤章、梅澤真史訳『リファクタリング 既存のコードを安全に改善する』オーム社、2014年
-
制御フロー - プログラムの実行順序を決定する構造。if文、for文、while文などの条件分岐や繰り返し処理を指す。 ↩
-
デバッグ - プログラムの不具合(バグ)を見つけて修正する作業。効率的なデバッグには読みやすいコードが不可欠。 ↩
-
冗長なコメント - 必要以上に長く、コードから明らかなことを繰り返すコメント。保守の負担になりやすい。 ↩
-
インラインコメント - コードの行末に記述する短いコメント。補足説明に使用されるが、多用は避けるべき。 ↩
-
専門用語 - プログラミング分野で広く認知されている技術用語。適切に使用することで簡潔な説明が可能になる。 ↩
-
エラーケース - 正常でない状態や例外的な状況。早期リターンで処理することでメインロジックが明確になる。 ↩
-
三項演算子 -
条件 ? 真の場合 : 偽の場合
の形式で記述する条件式。Pythonでは真の値 if 条件 else 偽の値
と記述する。 ↩ -
説明変数 - 複雑な条件式の一部を格納し、その意味を明確にするための変数。 ↩
-
要約変数 - 複雑な計算結果を保存し、再利用やデバッグを容易にするための変数。 ↩
-
短絡評価 - 論理演算において、結果が確定した時点で残りの評価を行わない最適化手法。andやorで使用される。 ↩
-
中間変数 - 計算の途中結果を一時的に保存する変数。必要性を検討し、不要なものは削除すべき。 ↩
-
スコープ - 変数が参照可能な範囲。グローバルスコープ、関数スコープ、ブロックスコープなどがある。 ↩
-
リファクタリング - 外部から見た動作を変えずに、コードの内部構造を改善する作業。 ↩
-
コードレビュー - 他の開発者がコードを確認し、品質向上のための指摘や提案を行うプロセス。 ↩