Pythonの名前マングリングとは?知らないとハマるクラス設計の落とし穴を徹底解説

公開日: 2025-10-19

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クラスの__dataChildクラスの__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)で十分です。 「この変数は外から触らない前提です」と明示しておくだけで、開発チーム内の意思疎通がスムーズになります。

一方で、ライブラリ開発や、他者が継承することを想定したクラス設計をするときには、名前マングリングがとても役立ちます。
外部からの誤使用を防ぎ、継承時の衝突も防げるので、クラスの安全性が高まるんです。

名前マングリングを使う際の注意点

使いどころを理解した上で、実際に使うときのポイントをいくつか挙げておきます。

  1. 外部アクセスを完全に禁止するものではないことを理解する_ClassName__attrの形でアクセスできてしまうので、「本当の意味でのprivate」ではありません。
  2. 継承構造が複雑なときにのみ活用する → シンプルなクラス設計では、むしろ読みづらくなることがあります。
  3. デバッグ時に__dict__を確認してみると理解が深まる → 変数がどう変換されているかを目で見るのが一番です。

まとめ:名前マングリングは「優しいカプセル化」

最後に、この記事のポイントを振り返ってみましょう。

  • 名前マングリングとは、Pythonがダブルアンダースコア付き変数名を_クラス名__変数名に変換する仕組み
  • 目的は、親子クラス間の変数名衝突を避けること
  • 完全なアクセス制御ではなく、「うっかり防止」のための工夫
  • 普段は_nameで十分。__nameは設計上の明確な理由があるときに使う

Pythonの設計は、堅苦しいルールではなく「人間の信頼関係」に近い柔らかさを持っています。 名前マングリングもその一部であり、「わざと触りにくくするけど、禁止はしない」というバランスが美しいんですよね。

もしあなたがPythonでクラス設計を学んでいる途中なら、一度この仕組みを実際に試してみると、Pythonがどれだけ開発者思いの言語なのかを感じられると思います。

ここまでお読みいただきありがとうございました!

Pythonの基礎から応用まで学べる
Python WebAcademy

Python WebAcademyでは、Pythonの基礎からアーキテクチャなどの応用的な内容まで幅広く学べます。
また、ブラウザ上で直接Pythonコードを試すことができ、実践的なスキルを身につけることが可能です。

Pythonの学習を始める