Pythonの名前マングリングとは?知らないとハマるクラス設計の落とし穴を徹底解説
Pythonをブラウザで実行しながら実践的に学ぶ
Pythonの基礎からソフトウェアアーキテクチャ,アルゴリズムなどの応用的な内容まで幅広く学べます。
ブラウザ上で直接Pythonコードを試すことができ、実践的なスキルを身につけることが可能です。
Pythonを学び始めてしばらく経つと、クラスを使ってコードを書く機会が増えてきますよね。 「クラス変数」や「インスタンス変数」、そして「カプセル化」という言葉も耳にするようになります。
そんな中で、ちょっとした違和感を覚える人も多いと思います。 たとえば、こんなコードを書いたときのこと。
class Sample:
def __init__(self):
self.__value = 10
obj = Sample()
print(obj.__value)
これを実行すると、Pythonは次のようなエラーを返します。
AttributeError: 'Sample' object has no attribute '__value'
「えっ?確かに
__value
を定義したのに、そんな属性はないってどういうこと?」
──初めてこの現象に出会ったとき、私(エンジニア歴10年)も頭の中が「???」となったのを覚えています。
この謎の挙動の正体こそが、名前マングリング(Name Mangling)なんです。
今回の記事では、名前マングリングについて詳しく解説します。
名前マングリングとは?¶
まず、「マングリング(Mangling)」という言葉からしてちょっと難しそうですよね。 直訳すると「ぐちゃぐちゃにする」「混ぜる」という意味があります。 つまり名前マングリングとは、Pythonがクラスの内部で属性名をこっそり書き換えている仕組みのこと。
先ほどの__value
は、実際にはPython内部で次のように変換されています。
_Sample__value
つまり、__value
という変数名は、クラス名Sample
と結合されて別の名前になっているんです。
これが名前マングリングの正体です。
では、なぜこんな面倒な仕組みがあるのでしょうか?
どうして名前マングリングが必要なのか?¶
Pythonの思想はとてもシンプルで、開発者を信頼するという文化があります。 JavaやC++のように「private」「protected」といった厳密なアクセス修飾子を持たない代わりに、Pythonでは「アンダースコア(_)」で意図を表現します。
ただし、クラスを継承したときに名前が衝突してしまうリスクがあるんですね。
例えば、次のようなコードを見てみましょう。
class Parent:
def __init__(self):
self.__data = "parent"
class Child(Parent):
def __init__(self):
super().__init__()
self.__data = "child"
obj = Child()
print(obj.__dict__)
このときの出力はどうなると思いますか?
結果は次の通りです。
{'_Parent__data': 'parent', '_Child__data': 'child'}
つまり、Parent
クラスの__data
とChild
クラスの__data
は、名前マングリングによって別々の変数として扱われるんです。
これは、意図せず親クラスの変数を上書きしてしまうバグを防ぐための仕組みです。 Pythonが「お互いのプライベート領域を守る」ために、裏でこっそり名前を変えてくれているというわけですね。
アンダースコアの意味を整理しよう¶
Pythonでは、アンダースコアの数によって意味が変わります。 混乱しやすいので、ここで一度整理しておきましょう。
書き方 | 名称 | 意味・用途 |
---|---|---|
_name |
シングルアンダースコア | 「内部用」を意味する慣習。外からアクセスはできる。 |
__name |
ダブルアンダースコア | 名前マングリングが起きる。外から直接アクセスできない。 |
__name__ |
特殊メソッド(マジックメソッド) | Pythonの内部処理に使われる予約名。例:__init__ 、__str__ など。 |
この中で「名前マングリング」が発生するのは、先頭にダブルアンダースコアがあり、末尾にアンダースコアがない場合だけです。
つまり、__init__
のような特別な名前はマングリングされません。
アンダースコアと特殊メソッドについてはについては、こちらの記事で詳しく解説しています。 👉 Pythonのアンダーバー(アンダースコア)とはなんなのか? 👉 Pythonの特殊メソッドとは?どんな種類があるのか?どうやって使うのか?
実際にどうアクセスできるのか?¶
さて、最初に戻って、obj.__value
がエラーになった例を思い出してください。
実は、内部で_Sample__value
という名前に変換されているので、次のように書けばアクセスできるんです。
class Sample:
def __init__(self):
self.__value = 10
obj = Sample()
print(obj._Sample__value) # 正しくアクセスできる
出力結果は以下のようになります。
10
これを見ると「なんだ、結局アクセスできるじゃん!」と思うかもしれません。 確かにそうです。
Pythonでは本当に触ってはいけないデータを強制的に隠すことはできません。 でも、うっかり上書きしてしまうリスクを減らすために、マングリングという緩やかな仕組みを用意しているんですね。
初心者がハマりやすい落とし穴¶
ここまで読んで「なるほど、そういう仕組みか」と思った方も多いでしょう。 ただ、実務でPythonを書いていると、名前マングリングはときどき思わぬ罠になります。
たとえば、テストコードや外部ライブラリとの連携をしているときに、クラスの内部変数を直接参照したくなることがあります。
そんなときに__
付きの変数を指定しても、見つからずにエラーになってしまうんです。
また、他の開発者が定義したクラスを継承してカスタマイズしようとするとき、「親クラスで__data
を使っているとは知らず、同じ名前で__data
を定義して上書きされない」という事態もよくあります。
このように、意図せぬ「すれ違い」が起こりやすいのが名前マングリングなんです。
名前マングリングを理解すると見えてくる「設計の美しさ」¶
ここまで仕組みを説明してきましたが、名前マングリングを理解するとクラス設計の考え方がぐっと深まります。 Pythonでは完全なカプセル化はあまり重視されていません。 むしろ「使う人を信頼して、必要なら触ってもいいよ」という哲学がベースにあります。
でも、「ここは基本的に外から触らないでね」という意思表示は必要です。 そのための記号がアンダースコアであり、その意思を「構文として」少しだけ強めにサポートしてくれるのが名前マングリングなんです!
エンジニア歴10年の私が感じる「使いどころ」¶
私自身、10年近くPythonを使ってきて感じるのは、名前マングリングを使う場面は、実はそんなに多くないということです。
多くのケースでは、シングルアンダースコア(例:_name
)で十分です。
「この変数は外から触らない前提です」と明示しておくだけで、開発チーム内の意思疎通がスムーズになります。
一方で、ライブラリ開発や、他者が継承することを想定したクラス設計をするときには、名前マングリングがとても役立ちます。
外部からの誤使用を防ぎ、継承時の衝突も防げるので、クラスの安全性が高まるんです。
名前マングリングを使う際の注意点¶
使いどころを理解した上で、実際に使うときのポイントをいくつか挙げておきます。
- 外部アクセスを完全に禁止するものではないことを理解する →
_ClassName__attr
の形でアクセスできてしまうので、「本当の意味でのprivate」ではありません。 - 継承構造が複雑なときにのみ活用する → シンプルなクラス設計では、むしろ読みづらくなることがあります。
- デバッグ時に
__dict__
を確認してみると理解が深まる → 変数がどう変換されているかを目で見るのが一番です。
まとめ:名前マングリングは「優しいカプセル化」¶
最後に、この記事のポイントを振り返ってみましょう。
- 名前マングリングとは、Pythonがダブルアンダースコア付き変数名を
_クラス名__変数名
に変換する仕組み - 目的は、親子クラス間の変数名衝突を避けること
- 完全なアクセス制御ではなく、「うっかり防止」のための工夫
- 普段は
_name
で十分。__name
は設計上の明確な理由があるときに使う
Pythonの設計は、堅苦しいルールではなく「人間の信頼関係」に近い柔らかさを持っています。 名前マングリングもその一部であり、「わざと触りにくくするけど、禁止はしない」というバランスが美しいんですよね。
もしあなたがPythonでクラス設計を学んでいる途中なら、一度この仕組みを実際に試してみると、Pythonがどれだけ開発者思いの言語なのかを感じられると思います。
ここまでお読みいただきありがとうございました!