LoginSignup
45
29

More than 5 years have passed since last update.

Pythonの疑似private変数とクラス継承によるトラブル

Last updated at Posted at 2017-08-25

Pythonのクラスにはメンバ変数のアクセス制限という概念がありません。全ての変数がpublicとして扱われます。

そこで、疑似private変数を作れるようになっているのですが、これをクラス継承と併用しようとするとバチこいたためトラブルシューティングとしてメモ。

error_case
class BaseClass(object):
    def __init__(self):
        self.__param = "I'm BaseClass"

class ChildClass(BaseClass):
    def hello(self):
        print self.__param

ChildClass().hello()
AttributeError                            Traceback (most recent call last)
<ipython-input-7-898f72a5b39a> in <module>()
----> 1 ChildClass().hello()
<ipython-input-6-9e0be3f6ef4d> in hello(self)
      5 class ChildClass(BaseClass):
      6     def hello(self):
----> 7         print self.__param
AttributeError: 'ChildClass' object has no attribute '_ChildClass__param'

疑似private変数

メンバ変数の名前の頭にアンダースコアを二つつけると、疑似的にprivate変数を作ることができる。

しかし内部的には、_{classname}__{変数名}という名前のpublic変数を用意して、クラス内からは__{変数名}という名前で参照できるようにしているだけ。それが「疑似」private変数である所以。

sample_program
class Sample(object):
    def __init__(self):
        self.__param = "I'm Sample"

sample = Sample()
print sample.__param
AttributeError                            Traceback (most recent call last)
<ipython-input-8-f5d0d60ad401> in <module>()
      4 
      5 sample = Sample()
----> 6 print sample.__param
AttributeError: 'Sample' object has no attribute '__param'
sample_program
sample = Sample()
print sample._Sample__param
"I'm Sample"

クラス継承との組み合わせ

一番上のエラーケースのように、 親クラス内のメソッドで設定した疑似private変数に、子クラスの内部からアクセスしようとすると失敗する

どうやら原因は疑似private変数の内部仕様による。

親クラス内で宣言した疑似メンバ変数は、それが継承されていようと、内部的にはself._BaseClass__paramに突っ込まれるらしい。

一方、子クラスからself.__paramを見ようとすると参照されるのは当然_ChildClass__paramなので、そんな変数ないよというエラーが帰ってきてしまうというカラクリ。

解決策

クラス継承を使うなら、親クラス内で一緒にゲッタを宣言しておく。

successful_case1
class BaseClass(object):
    def __init__(self):
        self.__param = "I'm BaseClass"

    def get_param(self):
        return self.__param

class ChildClass(BaseClass):
    def hello(self):
        print self.get_param()
"I'm BaseClass"

(更新) 親クラスのプロパティにする。外部からの直接のセットを防ぐことができる。

successful_case2
class BaseClass(object):
   def __init__(self):
       self.__param = "I'm BaseClass"

   @property
   def _param(self):
       return self.__param

class ChildClass(BaseClass):
   def hello(self):
       print self._param

滅多に起きないシチュエーションかもしれないが意外と不便。

version

2.7.12 |Anaconda 4.2.0 (x86_64)| (default, Jul 2 2016, 17:43:17) \n[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)]

45
29
6

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
45
29