LoginSignup
20

More than 1 year has passed since last update.

Pythonのアンダースコア2個で始まる名前は非公開だけでなくマングリング機構を利用してサブクラスに継承しないために使う

Last updated at Posted at 2021-01-14

Pythonの変数やメソッドをプライベート属性にするために、名前をアンダースコア2個で始める人や記事をみかけますが、「PEP8: Pythonコードのスタイルガイド」ではプライベート属性はアンダースコア1個で始めることを推奨してます。

PEP8での説明

  • _single_leading_underscore: "内部でだけ使う" ことを示します。 たとえば from M import * は、アンダースコアで始まる名前のオブジェクトをimportしません。
  • __double_leading_underscore: クラスの属性に名前を付けるときに、名前のマングリング機構を呼び出します (クラス Foobar__boo という名前は _FooBar__boo になります。以下も参照してください

一般的には、アンダースコアを名前の先頭に二つ付けるやり方は、サブクラス化されるように設計されたクラスの属性が衝突したときに、それを避けるためだけに使うべきです

マングリング機構

名前の最初をアンダースコア2個にすると、名前のマングリング機構(名前修飾)が働き、クラス固有の名前になります。
サブクラスで同じ名前を再定義しても別物として扱われ、サブクラスでの再定義(名前衝突)による影響を受けなくなります。
アンダースコア2個にしたのが原因で意図しない動作結果になることもありますのでご注意ください。
マングリング機構を利用したサンプルコードを以下に示します。

サンプルコード

以下のプログラムを実行すると、アンダースコア2個で始める名前は、サブクラスで同じ名前を定義しても別物として扱われ、処理が書いてあるクラスの属性に強制されることがわかります。

class Base:

    def __init__(self):
        self.value = "Base.value"
        self._value = "Base._value"
        self.__value = "Base.__value"

    def method(self):
        print("Base.method")

    def _method(self):
        print("Base._method")

    def __method(self):
        print("Base.__method")

    def base(self):
        print('base():')
        print(self.value)
        print(self._value)
        print(self.__value)  # サブクラスと名前衝突しても自分のを使う
        self.method()
        self._method()
        self.__method()  # サブクラスと名前衝突しても自分のを使う


class Sub(Base):

    def __init__(self):
        super().__init__()
        self.value = "Sub.value"
        self._value = "Sub._value"
        self.__value = "Sub.__value"

    def method(self):
        print("Sub.method")

    def _method(self):
        print("Sub._method")

    def __method(self):
        print("Sub.__method")

    def sub(self):
        print('sub():')
        print(self.value)
        print(self._value)
        print(self.__value)  # サブクラスと名前衝突しても自分のを使う
        self.method()
        self._method()
        self.__method()  # サブクラスと名前衝突しても自分のを使う


class SubSub(Sub):

    def __init__(self):
        super().__init__()
        self.value = "SubSub.value"
        self._value = "SubSub._value"
        self.__value = "SubSub.__value"

    def method(self):
        print("SubSub.method")

    def _method(self):
        print("SubSub._method")

    def __method(self):
        print("SubSub.__method")

    def subsub(self):
        print('subsub():')
        print(self.value)
        print(self._value)
        print(self.__value)  # サブクラスと名前衝突しても自分のを使う
        self.method()
        self._method()
        self.__method()  # サブクラスと名前衝突しても自分のを使う


subsub = SubSub()
subsub.base()
print()
subsub.sub()
print()
subsub.subsub()
実行結果
base():
SubSub.value
SubSub._value
Base.__value
SubSub.method
SubSub._method
Base.__method

sub():
SubSub.value
SubSub._value
Sub.__value
SubSub.method
SubSub._method
Sub.__method

subsub():
SubSub.value
SubSub._value
SubSub.__value
SubSub.method
SubSub._method
SubSub.__method

他言語との比較

C++, C#, Java でいうところの protected が Pythonでは アンダースコア1個、private が アンダースコア2個 に相当します。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20