Python「is_valid」の罠:関数名とその戻り値の型は一致させよう

優れた関数名は、その関数が「何をするか」を明確に伝えます。しかし、それと同じくらい重要なのが、**「何を返すか」**を名前に反映させることです。特に is_has_ で始まる関数名は、読む人に対して「この関数は真偽値(True or False)を返す」という暗黙の約束をします。

この約束を破ると、一見すると動作しているように見えて、実はバグを内包した非常に紛らわしいコードが生まれてしまいます。今回は、その具体的な失敗例と、あるべき姿について解説します。

目次

紛らわしい関数の具体例

あるCSVファイル名を受け取り、それが有効な形式であれば、レポート用のファイル名(_report.txt)に変換する、という処理を考えます。ここで、次のような関数を書いてしまいました。

# 悪い例:関数名と戻り値の型が一致していない
def is_valid_csv(filename: str):
    """
    ファイル名が有効かどうかをチェックし、
    有効であれば変換後のファイル名を、無効であれば元のファイル名を返す。
    """
    if filename.endswith(".csv"):
        # 有効な場合、変換した文字列を返す
        return filename.replace(".csv", "_report.txt")
    else:
        # 無効な場合、元の文字列を返す
        return filename

# 関数の呼び出し側
file_a = "data_2025.csv"
if is_valid_csv(file_a):
    print(f"ファイル'{file_a}'は有効です。処理を開始します。")

file_b = "archive.zip"
if not is_valid_csv(file_b):
    print(f"ファイル'{file_b}'は無効です。")

このコードを実行すると、file_a は「有効です」、file_b は「無効です」と正しく表示されているように見えます。しかし、ここには大きな罠が潜んでいます。

なぜこれが問題なのか?

呼び出し側の if is_valid_csv(file_a): というコードは、開発者には「is_valid_csvTrueを返したら」と読めます。しかし、実際にはこの関数は一度も TrueFalse を返していません。

Pythonでは、空でない文字列は True として、空文字列 ""False として評価されます(このような性質を「Truthiness」と呼びます)。このコードが動いているのは、is_valid_csv 関数が常に空でない文字列を返しているため、if文の評価が常に True になってしまうからです。if not is_valid_csv(file_b): の分岐は、実際には決して実行されることがありません。

is_valid_csv という名前は「検証する」と約束しているのに、実際には「文字列を変換する(あるいは何もしない)」という別の仕事をしており、戻り値の型も異なります。これが混乱とバグの元凶です。


解決策:責務を分離する

この問題を解決する原則は、**「一つの関数には一つの責務(責任)だけを持たせる」**ことです。つまり、「検証する関数」と「変換する関数」を明確に分けます。

1. 「検証」に徹した関数

まず、is_valid という名前にふさわしく、戻り値として必ず真偽値(bool)を返す関数を用意します。

# 良い例①:検証に特化した関数
def is_valid_csv_filename(filename: str) -> bool:
    """ファイル名が'.csv'で終わるかを検証し、真偽値を返す。"""
    return filename.endswith(".csv")

この関数は、ファイル名が有効かどうかの「はい/いいえ」に答えることだけに責任を持ちます。戻り値の型を -> bool と型ヒントで明記することで、その契約はさらに強固になります。

2. 「変換」に徹した関数

次に、ファイル名を変換する処理を、その役割が明確にわかる名前の別の関数として切り出します。

# 良い例②:変換に特化した関数
def convert_csv_to_report_name(filename: str) -> str:
    """CSVファイル名をレポート用ファイル名に変換する。"""
    return filename.replace(".csv", "_report.txt")

convert_ という動詞から、この関数が何かを「変換」し、文字列を返すことが容易に想像できます。

正しい呼び出し方

これら2つの関数を組み合わせることで、コードは誰が読んでも誤解の余地がない、明確で安全なものになります。

source_file = "data_2025.csv"

# 1. まず「検証」する
if is_valid_csv_filename(source_file):
    # 2. 検証が通ったら「変換」する
    report_file = convert_csv_to_report_name(source_file)
    print(f"'{source_file}'から'{report_file}'を生成します。")
else:
    print(f"'{source_file}'は無効なファイル名です。")

まとめ

関数の名前は、その関数が提供する機能の「仕様書」です。特に、is_has_ といった接頭辞は、戻り値が bool 型であることを強く示唆します。

  • is_valid と名付けたら、必ず bool を返す。
  • 文字列を操作・変換するなら、convert_format_ といった名前を付ける。
  • 一つの関数に「検証」と「変換」など、複数の責務を混ぜない。

この原則を守ることで、コードの可読性が向上し、予期せぬ挙動や発見しにくいバグを未然に防ぐことができます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次