慣習的な命名規則によるプライベートメンバ
アクセス修飾子とは、生成されたオブジェクトのメンバ(変数やメソッド)に対して外部からの参照や更新を制御するための修飾子のことです。オブジェクト指向プログラミングが可能な言語の多くでサポートされている機能なのですが、以前のページで書いたとおり、Pythonにはアクセス修飾子がありません。そのかわり、慣習的にアンダースコアで始まる名前 (例えば_xなど)のメンバは、プライベートなメンバとみなします。ただし、あくまでも「そうみなす」というだけなので外部からのアクセス自体は可能です。
以下、他の言語に馴染みのない方向けにアクセス修飾子に関して補足します。
以下のコードでは、User型の変数name、_ageを直接更新しています。_ageはprivateとみなしますが、外部から更新されています。
class User: """ ユーザークラス """ def __init__(self, name="", age=0): self.name = name # nameは公開 self._age = age # ageはprivateとみなす user = User("Yamada", 45) user.name = "Yoshida" user._age = 39 # 普通に外部から更新可能
一方、以下はJavaのコードです。同様の処理ですが、privateと書かれているメンバを外部から参照したり更新したりすることができません。
public class User { public String name; private int age; public User(String name, int age){ this.name = name; this.age = age; } } public class Main { public static void main(String[] args) { User user = new User("Yamada", 45); user.name = "Yoshida"; user.age = 39; } }
上のコードはUserクラスにname、ageが設定されています。nameはpublicというアクセス修飾子が設定されており、外部から参照・更新することが可能です。実際、16行目でnameをYoshidaに更新しています。一方、ageはprivateという外部からの更新を不許可にするアクセス修飾子が設定されており、17行目の更新でコンパイルエラーが発生します。大規模開発を行う際、他の開発者が外部から勝手にオブジェクトの状態を更新するような事故が多々発生するのですが、アクセス修飾子を適切に設定することにより、こういった不具合を防ぐことができます。
__変数名による隠蔽
ただしPythonのクラスは「アンダースコア2つで始まり、末尾がアンダースコア1つ以下」となる変数名の場合、その変数に対して外部からアクセスするとAttributeErrorを発生させることができます。この仕組みにより実質的なプライベートなメンバを設定することが可能です。
class Sample(): def __init__(self): self.a = 0 self._b = 0 self.__c = 0 self.__d_ = 0 self.__e__ = 0 obj = Sample() a = obj.a # アクセス可能 b = obj._b # アクセス可能 c = obj.__c # AttributeError発生 d = obj.__d_ # AttributeError発生 e = obj.__e__ # アクセス可能
上のサンプルでは、a、_bはアクセス可能です。_bはアンダースコアから始まっていますが、アクセスできてしまう点に注意してください。また、__c、__d_は外部からアクセスすると、AttributeErrorが発生します。
ただし、2つ以上のアンダースコアで終わるメンバはプライベートメンバとはなりません。(__init__などは特殊メソッドと呼ばれています。別ページにて解説します。)このため、__e__は名前の末尾がアンダースコア2つで終わるのでアクセス可能です。
とはいえ、前述の通り、完全なプライベート変数は存在しません。実は以下の方法よりアクセスできてしまいます。
c = obj._Sample__c # アクセスできてしまう
この原理はマングリングと呼ばれ、詳しくは公式ドキュメントの以下を参照してください。
https://docs.python.org/3/tutorial/classes.htmlより引用
クラスのプライベートメンバについて適切なユースケース(特にサブクラスで定義された名前との衝突を避ける場合)があるので、マングリング(name mangling) と呼ばれる、限定されたサポート機構があります。 __spam (先頭に二個以上の下線文字、末尾に一個以下の下線文字) という形式の識別子は、 _classname__spam へとテキスト置換されるようになりました。ここで classname は、現在のクラス名から先頭の下線文字をはぎとった名前になります。このような難号化 (mangle) は、識別子の文法的な位置にかかわらず行われるので、クラス定義内に現れた識別子全てに対して実行されます。名前のマングリングは、サブクラスが内部のメソッド呼び出しを壊さずにメソッドをオーバーライドするのに便利です。