ソフトウェア開発において、コードの可読性1は品質と保守性を左右する最も重要な要素の一つです。本記事シリーズでは、Pythonを題材として、読みやすく理解しやすいコードを書くための実践的なテクニック2を体系的に解説していきます。
全3回にわたって、コードの可読性を向上させる具体的な方法を、豊富な実例とともに紹介します。第1回では、コードの可読性の基本概念から、適切な命名規則3、そしてコードの見た目の美しさまでを扱います。
理解しやすいコード
優れたコードの定義
「優れた」コードとは何でしょうか。パフォーマンス4が良いコード? 短いコード? いいえ、最も重要なのは読みやすさです。
# === 悪い例:短いが意味不明 ===
def p(l):
return [x*2 for x in l if x>0]
# === 良い例:長いが意図が明確 ===
def process_positive_numbers(numbers):
"""正の数を2倍にして返す"""
doubled_positive_numbers = []
for number in numbers:
if number > 0:
doubled_positive_numbers.append(number * 2)
return doubled_positive_numbers
読みやすさの基本定理
コードは他の人が最短時間で理解できるように記述する必要がある
ここでいう「他の人」には、6ヶ月後の自分自身も含まれます。理解とは、コードを読んで変更・追加・バグ修正5ができるレベルを指します。
小さなことは絶対にいいこと?
コード量を減らすことが常に正解とは限りません。理解のしやすさを犠牲にしてまで短くする必要はありません。
# === 悪い例:過度に短縮したコード(論理演算子の悪用) ===
x = a > b and a - b or b - a
# === 良い例:明確な条件分岐 ===
if a > b:
absolute_difference = a - b
else:
absolute_difference = b - a
名前に情報を詰め込む
明確な単語を選ぶ
変数名や関数名には、できるだけ具体的で情報量の多い単語を使用します。汎用的な動詞(get、set、check など)6は避け、より具体的な動詞を選びましょう。
# === 悪い例:抽象的で情報量が少ない ===
def get():
return database.query("SELECT * FROM users")
# === 良い例:具体的で情報量が多い ===
def fetch_all_users_from_database():
return database.query("SELECT * FROM users")
よく使われる動詞の選択肢:
汎用的な動詞 | より具体的な選択肢 |
---|---|
get | fetch, retrieve, load, download |
set | save, store, commit, configure |
check | validate, verify, contains, exists |
change | update, modify, revise, transform |
汎用的な名前を避ける
tmp、retval、result などの汎用的な名前は情報量が少なく、コードの理解を妨げます7。
# === 悪い例:意味を持たない汎用的な名前 ===
def calculate_discount(price, rate):
tmp = price * rate
retval = price - tmp
return retval
# === 良い例:変数の目的が明確な名前 ===
def calculate_discount(price, rate):
discount_amount = price * rate
final_price = price - discount_amount
return final_price
名前に情報を追加する
変数名に単位や重要な属性を含めることで、誤用を防げます。
# === 悪い例:単位や制約が不明 ===
delay = 10
size = 1024
# === 良い例:単位や制約を名前に含める ===
delay_seconds = 10
max_file_size_mb = 1024
誤解されない名前
対になる言葉の使い方
プログラミングでは、対になる概念を表す単語のペアが多数存在します8。これらを正しく使い分けることで、コードの意図が明確になります。
概念 | 対になる言葉 | 使用例 |
---|---|---|
追加/削除 | add / remove | リストへの要素操作 |
開始/停止 | start / stop | プロセスやサービスの制御 |
作成/破棄 | create / destroy | オブジェクトのライフサイクル9 |
接続/切断 | connect / disconnect | ネットワークやDB接続 |
取得/設定 | get / set | プロパティアクセス |
限界値を含める場合の命名
数値の範囲を表す場合、その境界が含まれるかどうかを明確にする必要があります。
# === 悪い例:上限か下限か不明確 ===
if shopping_cart_size >= CART_LIMIT:
print("カートがいっぱいです")
# === 良い例:明確に上限値を示す ===
if shopping_cart_size >= MAX_ITEMS_IN_CART:
print("カートがいっぱいです")
ブール値の命名規則
ブール値10を表す変数には、is_
、has_
、can_
などの接頭辞を付けることで、その変数が真偽値であることを明示します。
# === 悪い例:ブール値かどうか不明 ===
authenticated = True
read = False
# === 良い例:ブール値であることが明確 ===
is_authenticated = True
has_read_permission = False
can_edit_document = True
美しさ
なぜ美しさが大切なのか
コードの見た目の美しさは、単なる美学の問題ではありません。整然としたコードは読みやすく、バグも見つけやすくなります11。
# === 読みにくい例 ===
class StatsKeeper:
def __init__(self):
self.count=0
self.minimum=0
self.maximum=0
def Add(self,d):
...
def Average(self):
...
# === 読みやすい例 ===
class StatsKeeper:
def __init__(self):
self.count = 0
self.minimum = 0
self.maximum = 0
def add(self, data):
"""データを追加する"""
...
def average(self):
"""平均値を計算して返す"""
...
一貫性のある配置
同じような処理は同じような見た目にすることで、パターンが認識しやすくなります。
# === 悪い例:一貫性のない改行 ===
user1 = User("Alice", 25, "alice@example.com")
user2 = User("Bob", 30,
"bob@example.com")
user3 = User("Charlie",
35, "charlie@example.com")
# === 良い例:一貫した改行 ===
user1 = User("Alice", 25, "alice@example.com")
user2 = User("Bob", 30, "bob@example.com")
user3 = User("Charlie", 35, "charlie@example.com")
コードを「段落」に分割する
空行を使ってコードを論理的なブロックに分けることで、全体の構造が把握しやすくなります12。
def process_user_data(user_id):
# ユーザー情報の取得
user = get_user(user_id)
if not user:
return None
# 関連データを取得
profile = get_profile(user_id)
preferences = get_preferences(user_id)
recent_activities = get_recent_activities(user_id)
# 結果をまとめて返す
result = {
"user": user,
"profile": profile,
"preferences": preferences,
"activities": recent_activities
}
return result
コメントすべきことを知る
コメントするべきでは「ない」こと
コードから明らかなことをコメントで繰り返すのは冗長です13。
# === 悪い例:コードから明らかなことをコメント ===
# iに1を加える
i += 1
# userという変数にget_user()の結果を代入
user = get_user()
# === 良い例:なぜそうするのかを説明 ===
# ユーザーのログイン試行回数を増やす
login_attempts += 1
# キャッシュを確認してからDBアクセス
user = get_user() # 内部でキャッシュを利用
自分の考えを記録する
コードの「なぜ」を説明することが重要です。実装の選択理由や、既知の問題をTODOコメント14として残しましょう。
def quick_sort(arr):
"""
クイックソートアルゴリズムを実装
選択理由:
- 平均的にO(n log n)の時間計算量
- インプレースソートで追加メモリが不要
注意事項:
- 最悪の場合はO(n²)になる
- TODO: 大規模データセットではヒープソートへの切り替えを検討
"""
pass
まとめ
第1回では、Pythonを通じて学ぶリーダブルコードの基礎となる5つの重要な概念を解説しました:
- 理解しやすいコード - 読みやすさを最優先に考える
- 名前に情報を詰め込む - 変数名・関数名から意図が伝わるようにする
- 誤解されない名前 - 曖昧さを排除し、明確な名前を付ける
- 美しさ - 一貫性のある整然としたコードレイアウト
- コメントすべきことを知る - 「なぜ」を説明し、明らかなことは書かない
これらの原則を実践することで、あなたのPythonコードは格段に読みやすくなり、チーム全体の生産性向上につながります。
参考文献
- Dustin Boswell、Trevor Foucher著、角征典訳『リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック』オライリー・ジャパン、2012年
- Robert C. Martin著、花井志生訳『Clean Code アジャイルソフトウェア達人の技』アスキー・メディアワークス、2009年
- Steve McConnell著、日向俊二訳『Code Complete 第2版 完全なプログラミングを目指して』日経BP社、2005年
-
可読性 - コードがどれだけ読みやすく、理解しやすいかを表す指標。保守性や拡張性に直結する重要な品質特性。 ↩
-
実践的なテクニック - 理論だけでなく、実際の開発現場で即座に適用できる具体的な手法やパターン。 ↩
-
命名規則 - 変数、関数、クラスなどの名前付けに関する一貫したルール。camelCase、snake_case、PascalCaseなどがある。 ↩
-
パフォーマンス - プログラムの実行速度やメモリ使用効率などの性能指標。 ↩
-
バグ修正 - プログラムの不具合(bug)を特定し、正しい動作をするように修正すること。デバッグ(debugging)とも呼ばれる。 ↩
-
汎用的な動詞 - get、set、checkなど、多くの文脈で使用される抽象度の高い動詞。具体性に欠けるため、コードの意図が不明確になりやすい。 ↩
-
コードの理解を妨げる - 変数の役割や目的が不明確になり、コードを読む人が推測や調査に時間を費やすことになる状態。 ↩
-
対になる概念 - プログラミングにおいて頻繁に登場する反対の意味を持つ操作のペア。一貫性を保つことで可読性が向上する。 ↩
-
ライフサイクル - オブジェクトが生成されてから破棄されるまでの一連の過程。create→use→destroyという流れ。 ↩
-
ブール値 - true(真)またはfalse(偽)の2つの値のみを取るデータ型。条件判定に使用される。 ↩
-
バグを見つけやすくなる - コードが整理されていると、異常なパターンや不整合が視覚的に目立つため、問題の早期発見につながる。 ↩
-
論理的なブロック - 関連する処理をまとめたコードの単位。段落分けすることで、各ブロックの役割が明確になる。 ↩
-
冗長 - 必要以上に長く、くどいこと。コメントの場合、情報価値のない繰り返しを指す。 ↩
-
TODOコメント - 将来的に実装や改善が必要な箇所を示すコメント。
TODO:
で始めるのが一般的な慣習。 ↩