優れた関数名は、その関数が「何をするか」を明確に伝えます。しかし、それと同じくらい重要なのが、**「何を返すか」**を名前に反映させることです。特に 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_csv
がTrue
を返したら」と読めます。しかし、実際にはこの関数は一度も True
や False
を返していません。
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_
といった名前を付ける。 - 一つの関数に「検証」と「変換」など、複数の責務を混ぜない。
この原則を守ることで、コードの可読性が向上し、予期せぬ挙動や発見しにくいバグを未然に防ぐことができます。