<一覧に戻る

マジックナンバーの排除

「この数字、何を意味しているんだろう?」と感じたことはありませんか?

コードの中に突然あらわれる謎の数値は、読み手を迷わせ、後から自分で読み返したときにも混乱のもとになります。Python初心者がつまずきやすいポイントのひとつが、この「マジックナンバー」です。

ここでは、マジックナンバーとは何か、なぜ避けるべきなのか、そしてどう置き換えれば“読んでわかるコード”になるのかを、実例とともに丁寧に学んでいきましょう。

マジックナンバーとは?

まず用語をしっかり押さえましょう。

マジックナンバーとは、コード内に突然出てくる、意味の説明がない数値のことです。意味がわからない数字があると、読む人は「なぜこの値?」と考え込むことになります。

たとえば次のコードを見てみましょう。

def calculate_area(radius):
    return 3.14 * radius * radius

このコードでは、3.14がマジックナンバーです。円周率だとわかる人もいますが、全員がそうとは限りません。 さらに、将来より正確な値に変えたいとき、どこを直せばいいのか探す手間も増えます。

マジックナンバーを避けるべき理由

ここで、なぜマジックナンバーを排除すると良いのかを整理しておきます。背景を理解しておくと、日々のコードでも意識しやすくなります。

  • 【可読性が上がる】 数値に名前が付くことで、「何を表す値か」を一瞬で理解できるようになります。読む人への親切は、将来の自分への親切です。
  • 【メンテナンスが楽になる】 値を変えたいときに、定数を一箇所変更するだけで全体に反映されます。見落としによる不具合を防げます。
  • 【バグを防止できる】 意味を明示することで誤解が減り、数字の取り違え・コピペミスを防げます。

マジックナンバーを定数に置き換える

では、実際にどのように置き換えればよいのかを見ていきましょう。

先ほどの例を、意味のある名前を持つ定数に置き換えます。

PI = 3.14  # 円周率

def calculate_circle_area(radius):
    return PI * radius * radius

ここでは、3.14PIという名前を付けました。これで「この値は円周率なんだ」とコードが語ってくれるようになります。もし後から値をより正確にしたい場合も、PIを一箇所変えるだけで済みます。

さらに精度と信頼性を高めたいなら、Python標準ライブラリを活用するのがおすすめです。数値を自分で書かず、公式に用意された定数を使うと安心です。

import math

def calculate_circle_area(radius):
    return math.pi * radius * radius

math.piは高精度な円周率を提供してくれます。「どれが正しい値か」を悩む必要がありません。

例で学ぶ:複数の円の面積を合計する

単体の関数だけでなく、複数の関数にまたがってマジックナンバーを排除すると効果を実感しやすくなります。次のプログラムは、複数の半径から円の面積を合計します。

import math

def calculate_circle_area(radius):
    return math.pi * radius * radius

def calculate_total_area(radii):
    total_area = 0.0
    for radius in radii:
        total_area += calculate_circle_area(radius)
    return total_area

radii_list = [1, 2, 3, 4]
total_area = calculate_total_area(radii_list)
print("Total area:", total_area)

ここでは、円周率を関数内に書かずmath.piを使っています。もし円周率の定義を変える必要があれば、math.piを使う方針のまま保てば、あちこちの数値を探す必要はありません。プログラム全体の「変更に強い設計」につながります。

定数の置き場所と命名のコツ

ここからは、実務や学習でそのまま役立つ実践ポイントを押さえていきましょう。どこにどう書けば読みやすくなるのか、迷ったことはありませんか。

  • 【定数はモジュールの先頭にまとめる】 ファイルの上部に定数を置くと、設定値や重要なパラメータがひと目でわかります。プロジェクトが大きくなるほど効いてきます。
  • 【名前は「何を表すか」がわかるように】 PIMAX_RETRYTAX_RATESECONDS_PER_MINUTEのように、読むだけで意味が想像できる名前にしましょう。
  • 【大文字スネークケースを使う】 Pythonでは「定数は大文字+アンダースコア」が慣習です。たとえばDEFAULT_TIMEOUT = 5.0のように書くと、他の人も「これは変えない前提の値だな」とすぐに理解できます。

次のコードは、いくつかのよくある設定値を定数化した例です。どれも、「その数字が何を表すか」を名前が説明してくれています。

import math

MAX_RETRY = 3              # リトライの最大回数
DEFAULT_TIMEOUT = 5.0      # 秒
DISCOUNT_RATE = 0.10       # 10%
SECONDS_PER_MINUTE = 60    # 単位変換

def should_retry(attempt):
    return attempt < MAX_RETRY

def apply_discount(price):
    return price * (1 - DISCOUNT_RATE)

def to_seconds(minutes):
    return minutes * SECONDS_PER_MINUTE

def circle_area(radius):
    return math.pi * radius * radius

これらはどれも、ただの「3」「5.0」「0.1」「60」より、はるかに読みやすく安全です。あなたの現場や学習中のコードにも、似た“生の数字”が眠っていませんか。

マジックナンバー以外の「マジック値」にも注意

数字だけが問題ではありません。実は文字列や特殊な値が、静かにバグの原因になることがあります。 たとえば、次のようなコードを見たことはありませんか。

status = "OK"  # 他の場所では "SUCCESS" を使っているかも?

このような「マジックストリング」は表記ゆれが起きやすく、タイポ(タイピングミス)にも弱いです。定数にして意味を固定すると安心です。

STATUS_OK = "OK"
STATUS_ERROR = "ERROR"

def is_ok(status):
    return status == STATUS_OK

文字列の種類が増えてきたら、列挙型で表現するとさらに安全になります。初心者でも次のように書けます。

from enum import Enum, auto

class Status(Enum):
    OK = auto()
    ERROR = auto()

def is_ok(status: Status) -> bool:
    return status is Status.OK

これなら誤った文字列を渡してもエラーで気づけるようになります。

よくあるマジックナンバーの置き換え例

この先の学習や実務で出会いやすいケースを知っておくと、実装中に「これは定数化しよう」と気づきやすくなります。以下は実践でよく使うパターンです。

パスワードの長さチェックの例

まずはパスワードの長さチェックの例です。 セキュリティ関連の処理では「最低8文字」といったルールをよく見かけますが、数値をそのまま書いてしまうと、後で仕様が変わったときに混乱のもとになります。

# 悪い例
if len(password) < 8:
    raise ValueError("Too short")

# 良い例
MIN_PASSWORD_LENGTH = 8

if len(password) < MIN_PASSWORD_LENGTH:
    raise ValueError("Too short")

このように、8という数字に意味を持たせてMIN_PASSWORD_LENGTHという定数に置き換えると、「8が何を表すのか」が一目でわかります。 また、要件が「10文字以上」に変わっても、定数を1か所変更するだけで済みます。

一定時間待機する処理の例

次に、一定時間待機する処理の例を見てみましょう。 リトライ処理やAPIの呼び出し間隔で、待機時間を直接書いてしまうのもよくあるマジックナンバーのパターンです。

# 悪い例
sleep(3)

# 良い例
DEFAULT_RETRY_DELAY = 3  # 秒
sleep(DEFAULT_RETRY_DELAY)

「3秒待つ」という仕様がコード上にそのまま書かれていると、読み手には意図が伝わりにくくなります。 DEFAULT_RETRY_DELAYという定数名を使えば、「これはデフォルトの待機時間なんだな」と一瞬で理解できます。

ログイン試行回数の上限の例

最後は、ログイン試行回数の上限の例です。 セキュリティや認証まわりのコードでは、「何回まで失敗を許すか」を定義することがよくあります。

# 悪い例
if attempts > 5:
    lock_user()

# 良い例
MAX_LOGIN_ATTEMPTS = 5
if attempts > MAX_LOGIN_ATTEMPTS:
    lock_user()

このように、5という数字をMAX_LOGIN_ATTEMPTSに置き換えるだけで、 「この値が“最大ログイン試行回数”を意味しているんだ」とすぐに理解できるようになります。

読み手にとってなぜこの数なのかが伝わるだけで、コードの意図がクリアになります。

まとめ:今日からできるチェックリスト

最後に、毎日のコーディングでサッと見直せる観点をまとめます。読みやすさと保守性をぐっと高めるために、実装の前後で次の点を確認してみてください。

  • 同じ数値や文字列を複数箇所で使っていないか
  • 値にドメイン上の意味(上限、下限、閾値、秒数、割合)があるか
  • その数値を見ただけで意図が伝わるか、それとも名前が必要か
  • 標準ライブラリで代替できる定数や機能(例: math.pi)がないか
  • 定数はモジュール先頭にまとめ、わかりやすい大文字名にしているか

次にコードを書くとき、「この数字は他の人に意味が伝わるかな?」と自分に問いかけてみてください。マジックナンバーを排除するだけで、Pythonのコードはぐっと読みやすく、将来の変更にも強くなります。あなたのコードを、明日の自分とチームが安心して読める“ドキュメント”に育てていきましょう。

このトピックの他のセクション:

  1. Pythonの命名規則
  2. 意味のある変数名・関数名を使う
  3. マジックナンバーの排除(現在表示中)
  4. コメントの活用
  5. 冗長性の排除
  6. 関数の分割

出力結果: