閉じる

これだけで分かるPythonの_の使い方(一時変数/国際化/プライベート)

Pythonではアンダースコア( _ )が多目的に使われます。

本記事では一時変数として値を捨てる使い方、国際化(i18n)の翻訳関数としての使い方、プライベート変数の目印としての使い方を、初心者向けに丁寧に解説します。

どれも日常的に登場するため、使い分けを理解しておくとコードの可読性と保守性が高まります。

以下の表は本記事の要点を先にまとめたものです。

用途代表的な書き方目的主な注意点
一時(捨て)変数for _ in range(n), (x, _)不要な値を明示的に無視する意味のある値を入れない。i18nの_()と競合注意
国際化(i18n)_ = gettext.gettext, _("...")メッセージ文字列を翻訳する捨て変数の_と同ファイル併用しない。REPLの_とは別物
プライベート目印_name, __name内部用/名前マングリング絶対的な秘匿ではない。PEP8とimport *の挙動理解

_ を一時変数として使う

捨てたい値に_を代入して「使わない」意思を示すのがPythonの慣習です。

PEP8でも_はダミー名として一般的で、静的解析ツールも「未使用変数」として許容することが多いです。

for _ in range(n) で値を捨てる

ループ変数が不要な場合に_を使います。

意図が一目でわかり、読み手に親切です。

Python
# 3回だけ処理したいが、カウンタ値自体は使わない例
for _ in range(3):
    print("tick")
実行結果
tick
tick
tick

タプルのアンパッキングで不要要素を _ に入れる

戻り値などの一部だけ使いたい時に、不要部分を_に受けます。

Python
# 緯度経度高度のタプルから、高度は捨てて緯度と経度だけ使う
def get_position():
    return (35.6895, 139.6917, 44)  # lat, lon, altitude

lat, lon, _ = get_position()  # 高度は捨てる
print(f"lat={lat}, lon={lon}")
実行結果
lat=35.6895, lon=139.6917

enumerate で index や値を無視する( _, v )/( i, _ )

enumerateの戻りは(index, value)です。

どちらか一方だけ必要なら_で捨てます。

Python
names = ["Ada", "Grace", "Guido"]

# indexは不要
for _, name in enumerate(names):
    print(name.upper())

# 値は不要で、indexだけ使いたい
for i, _ in enumerate(names):
    print(f"position = {i}")
実行結果
ADA
GRACE
GUIDO
position = 0
position = 1
position = 2

複数要素を無視する慣習( _ を繰り返し使う)

_はあくまで「使わない」という印(しるし)なので、複数あっても同じ_を繰り返し使います。

Python
# 5要素の戻り値のうち、先頭と末尾だけ使う
def complex_result():
    return (1, "ignore1", "ignore2", "ignore3", 99)

head, _, _, _, tail = complex_result()
print(head, tail)
実行結果
1 99

補足: Pythonには「スター付きアンパッキング」(例: head, *_, tail = seq)もありますが、本記事の主題外なので名称のみの紹介に留めます。

注意: _ は「使い捨て」専用にして意味のある値を入れない

意味のある値を_に代入しないでください。

読み手に「ここは無視される」と誤解させ、深刻なバグの温床になります。

特に、後述のi18nで_を翻訳関数にした後にfor _ in range(...)を同ファイルで書くと壊れます。

Python
# 悪い例: 先に _ を翻訳関数として定義したのに、あとで捨て変数に使ってしまう
from gettext import gettext as _

print(_("Start"))  # ここはOK

for _ in range(3):  # ここで _ の束縛が int に上書きされる(モジュールスコープの場合)
    pass

print(_("End"))  # TypeError: 'int' object is not callable になる
実行結果
Traceback (most recent call last):
  ...
TypeError: 'int' object is not callable

回避策: i18nで_を使うファイルでは、捨て変数に_を使わず___unusedなど別名にするのが安全です。

_ を国際化(i18n)で翻訳関数として使う

多くのPythonプロジェクトでは翻訳関数に_という短い別名を与える慣習があります。

短いのでメッセージ文字列が読みやすく、i18n対応がしやすくなります。

gettext で _ = gettext.gettext を定義する

標準ライブラリgettextを使うと、翻訳資源(拡張子.mo)があれば自動で翻訳されます。

資源がなければ原文のまま表示されます。

Python
# i18nの最小構成例: 資源がなければ原文のまま(フォールバック)
import gettext

# 実際には 'locale/<言語>/LC_MESSAGES/messages.mo' を用意します
localedir = "locale"
domain = "messages"
lang = "ja"

try:
    trans = gettext.translation(domain=domain, localedir=localedir, languages=[lang])
    _ = trans.gettext  # 慣習的に _ に束縛
except FileNotFoundError:
    _ = gettext.gettext  # フォールバック(翻訳なし)

print(_("Hello"))
name = "Taro"
# 変数埋め込みは f-string ではなく後段で format するのが定石
print(_("Hello, {name}!").format(name=name))
実行結果
Hello
Hello, Taro!

ポイント: f-string で_("Hello, {name}!")を囲むと翻訳時点で変数が展開されてしまい、翻訳キーが一致しなくなる恐れがあります。

プレースホルダを残したまま翻訳してから.formatで差し込むのが安全です。

メッセージを _(“…”) で囲む基本パターン

_("メッセージ")で囲んだ文字列が翻訳抽出対象になります。

複数箇所で使うメッセージは、同一の原文に統一すると訳が再利用され品質が安定します。

Python
# よくある利用パターン
print(_("File not found"))
print(_("Permission denied"))
実行結果
File not found
Permission denied

注意: 捨て変数の _ と競合させない(同一ファイルで併用しない)

同じファイルで「捨て変数の_」と「翻訳関数の_」を併用しないでください。

特にモジュールスコープのfor _ in ...は翻訳関数を上書きします。

どうしても必要なら、翻訳関数に_tLなど別名を使います。

Python
# 競合を避けるための別名パターン
from gettext import gettext as _t  # 翻訳関数は _t
for _ in range(3):  # こちらは捨て変数として _
    pass
print(_t("Done"))
実行結果
Done

注意: REPL の直前結果の _ とは別物である点に注意

対話実行環境(REPL)では、_が「直前の評価結果」を指す特別名として使えますが、スクリプトでは単なる通常の識別子です。

Python
# (REPLの例) 対話モードだけの振る舞い
# >>> 1 + 2
# 3
# >>> _
# 3
# >>> _ + 10
# 13

# スクリプトでは上記のような自動束縛は起きません。
# _ は自分で代入した時だけ意味を持ちます。

混同しないコツ: REPLで_を手動で再代入するのは避け、スクリプトでは用途ごとに命名規則を守ると安全です。

_ をプライベート変数の目印に使う

先頭にアンダースコアを付けると「公開APIではない」意思表示になります。

Pythonはアクセス制御を強制しませんが、PEP8に沿った慣習として広く使われています。

先頭1つのアンダースコア(_name)は「内部用」の慣習

_nameはモジュールやクラスの内部実装用であることを示します。

アクセスは技術的には可能ですが、外部から触らないのが前提です。

Python
class Cache:
    def __init__(self):
        self._store = {}  # 内部用
    def set(self, key, value):
        self._store[key] = value
    def get(self, key, default=None):
        return self._store.get(key, default)

c = Cache()
c.set("x", 10)
print(c.get("x"))
# 可能だが非推奨: 外部から _store を直接触る
print("internal keys =", list(c._store.keys()))
実行結果
10
internal keys = ['x']

先頭2つのアンダースコア(__name)は名前マングリング

__nameはクラス定義内で名前マングリングされ、実際には_ClassName__nameという内部名に置き換えられます。

サブクラスからの誤った上書きを防ぎやすくなります。

Python
class User:
    def __init__(self, token: str):
        self.__token = token  # マングリング対象
    def masked(self) -> str:
        return self.__token[:2] + "***"

u = User("ABCD1234")
print(u.masked())

# 直接アクセスは失敗する
try:
    print(u.__token)
except AttributeError as e:
    print("AttributeError:", e)

# 実際の属性名(マングリング後)を使えば参照できてしまう(非推奨)
print("mangled access =", u._User__token)

# 確認
print([name for name in dir(u) if "token" in name])
実行結果
AB***
AttributeError: 'User' object has no attribute '__token'
mangled access = ABCD1234
['_User__token']

補足: __init____str__のように前後をダブルアンダースコアで挟む「マジックメソッド」は特別扱いで、通常のマングリング対象とは目的が異なります。

絶対的な非公開ではないが外部から触らないのが原則

アンダースコアは契約(コントラクト)を文書化するための信号です。

動的言語の理念に沿い、強制ではなく合意で守ります。

テストやデバッグで内部に触れる時は、影響範囲を把握し自己責任で行います。

PEP8 に沿った命名と from module import * の挙動を理解する

PEP8では_internalのような先頭アンダースコアは「非公開」を意味し、from module import *では先頭_の名前はインポートされません

さらに__all__を定義すると公開シンボルを明示できます。

以下は2ファイル構成の例です。

Python
# mylib.py
__all__ = ["public_func"]  # 公開APIを明示

_public = "visible-but-not-exported"  # 先頭1つ -> import * では除外(ただし __all__ があればそちらが優先)
_internal = 42  # これも非公開意図

def public_func():
    return "I am public"

def _helper():
    return "I am internal"
Python
# main.py
from mylib import *

print(public_func())

# 非公開名は import * で入ってこない
names = [n for n in dir() if n.endswith(("helper", "_internal", "_public"))]
print("imported names:", names)

# 明示的に import すれば取れるが、慣習上は避ける
import mylib
print("explicit:", mylib._helper(), mylib._internal)
実行結果
I am public
imported names: []
explicit: I am internal 42

指針:

  • ライブラリ作者は__all__で公開APIを明示すると利用者に優しいです。
  • 利用者側はfrom module import *を避け、import modulefrom module import nameのように明示すると可読性が上がります。

まとめ

本記事ではアンダースコア( _ )の3つの主要用途を紹介しました。

捨て変数としての_は「使わない」意思の明確化に役立ち、i18nでは_ = gettext.gettextの慣習により翻訳文字列が簡潔に書けます。

プライベートの目印としての_name__nameは公開範囲を示し、PEP8やimport *の挙動とも整合します。

最後にもう一度だけ注意点を整理します。

i18nの_と捨て変数の_を同じファイルで混在させない意味のある値を_に入れない、そしてアンダースコアは強制ではなく契約であるという3点です。

これらを守れば、読みやすく安全なPythonコードに一歩近づけます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!