Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Pythonのダンブアンダースコア変数とマングリング

【Python入門】クラス内のアンダースコア2つの「__変数」とマングリング機能の解説

【Python入門】クラス内のアンダースコア2つの「__変数」とマングリング機能の解説

今回はPythonのクラス内部で使われる特殊な変数の名前の付け方について解説します。その特殊な名前とは、変数名の前にアンダースコアが2つついているものです。たとえば、

  • __price
  • __size

といったものです。なおアンダースコアを2つ連続させることをダブルアンダースコア、略してダンダーと呼ぶことが多いようです。

さてこうした「__変数」がクラス内部で使われた場合に、Pythonはマングリングという仕組みを発動させ、その変数名を変化させます

今回はこのようなクラス内部の「__変数」とマングリングについて解説します。

公式サイトの記述

まず公式サイトにはこのマングリングについて、

プライベートな名前のマングリング: クラス定義内に書かれた識別子で、2つ以上のアンダースコアから始まり、末尾が2つ以上のアンダースコアで終わっていないものは、そのクラスの プライベートな名前 とみなされます。プライベートな名前は、コードが生成される前により長い形式に変換されます。この変換によって、クラス名の先頭にアンダースコアがあれば除去し、先頭にアンダースコアを1つ付加し、名前の前に挿入されます。例えば、クラス名 Ham の中の識別子 __spam は、_Ham__spam に変換されます。変換は識別子が使用されている構文のコンテキストからは独立しています。変換された名前が非常に長い (255文字を超える) 場合、実装によっては名前の切り詰めが行われるかもしれません。クラス名がアンダースコアのみから成る場合は変換は行われません。

(太字化は1978Worksによる)

Python公式ドキュメント: 6.2.1. 識別子 (identifier、または名前 (name))

と書かれています。

この公式説明の日本語で、最も日本語が不自由・不自然になっててわかりにくいのが、

クラス名の先頭にアンダースコアがあれば除去し、先頭にアンダースコアを1つ付加し

この部分です。しかし太字化した「例えば~」の記述を見てもらえれば、この日本語が不自由な部分は、

  • クラス名の先頭にアンダースコアがある場合は、それを全て除去したうえで、先頭にアンダースコアを1つつけた名前にする。例えば、アンダースコアを3つ使ったクラス名「___Book」は最終的に「_Book」となります。
  • クラス名の先頭にアンダースコアがない場合は、単に先頭にアンダースコアを1つつける。例えば「Book」→「_Book」

と読み替えます。

これさえわかれば特にどうこう解説する必要はないのかもしれませんが、以下で簡単なコード例でマングリングを確認してみましょう。

マングリングのコード例と解説

具体例として、Bookというクラス内部に、

  • price
  • __price

というよく似た名前の変数を2つ使うコードを書いてみました。次のコードを見てください。

class Book:
    def set_price(self, yen):
        # ダンダーなし→マングリングは発動しない
        self.price: int = yen
        # ダンダーあり→マングリングが発動して、「__price」→「_Book__price」へ
        self.__price: int = yen*2
        
    def get_price(self):
        # タプル形式で両方の価格を返す
        return (self.price, self.__price*10)

comic = Book()
comic.set_price(10)

prices: tuple = comic.get_price()
print(prices)  # ==> (10, 200)

このクラスBook内部には2つのメソッドが定義されていて、

  • set_price→価格情報を設定するだけのメソッド
  • get_price→〃を取得するだけ〃

という機能分担になっています。基本的なコードなので理解されるのに特に問題はないと思います。

ではここでインスタンスcomicの属性を確認してみましょう。組込関数のdir()を使って、次のように書きます。

dir(comic)

実行した結果は、 次のようになります。

Pythonのクラスとマングリングとダブルアンダースコア

画像内部の2つの黄色下線部をみてください。

  • _Book__price
  • price

という2つの属性名が表示されています。最初の「_Book__price」はまさに、マングリング機能によって「__price」から変換されたものです。

では続いて、試しに次のようなコードを加えてみましょう。

comic.__price = 20

では、再びインスタンスcomicの属性リストを見てみましょう。

dir(comic)
Pythonのクラスとマングリングとダブルアンダースコア

黄色下線のところに、新たな属性名として「__price」というのが登場しています。また「_Book__price」と「price」も存在したままです。

すなわち、

comic.__price = 20

このようにして設定した「comic.__price」と、もともとのクラス内部の「__price」は全く別の独立した存在であるということです。なぜならマングリング機能によって、クラス内部の「__price」は「_Book__price」として扱われていますから。

マングリングのメリットとは?

以上の説明でマングリングのメリットを理解された方もいるでしょうが、マングリングのメリットとはまさに、クラス内部の情報を、クラスの外部からうっかりミスでいじってしまうのを防ぐことです。

クラスから複数のインスタンスが生成されるわけですが、クラスの内部の情報をいじればそれらインスタンスも影響を受けてしまい、それらインスタンスの情報までが全て一気に変わってしまいます

クラスに対する1つのミスが、コード中のいくつものインスタンスに影響を与えてしまい修正作業がかなり面倒なものになるのは想像に難くないでしょう。

クラス外部からクラス内部の変数を編集できないようにするのをプライベート化とも言えるでしょうが、Pythonは他のプログラミング言語とは異なり、プライベート化の仕組みをそもそも持っていないため、マングリング機能によってプライベート化に近い働きを代替していると考えられます。

ただしあくまで「プライベート化に近い働き」にすぎないため、完全にクラス外部からアクセスできなくしたり、編集できなくなっているわけではありません。この点は注意しましょう。

愛を分かち合いましょう