Pythonのクラスにはメンバ変数のアクセス制限という概念がありません。全ての変数がpublicとして扱われます。
そこで、疑似private変数を作れるようになっているのですが、これをクラス継承と併用しようとするとバチこいたためトラブルシューティングとしてメモ。
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変数である所以。
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 = Sample()
print sample._Sample__param
"I'm Sample"
クラス継承との組み合わせ
一番上のエラーケースのように、 親クラス内のメソッドで設定した疑似private変数に、子クラスの内部からアクセスしようとすると失敗する。
どうやら原因は疑似private変数の内部仕様による。
親クラス内で宣言した疑似メンバ変数は、それが継承されていようと、内部的にはself._BaseClass__param
に突っ込まれるらしい。
一方、子クラスからself.__param
を見ようとすると参照されるのは当然_ChildClass__param
なので、そんな変数ないよというエラーが帰ってきてしまうというカラクリ。
解決策
クラス継承を使うなら、親クラス内で一緒にゲッタを宣言しておく。
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"
(更新) 親クラスのプロパティにする。外部からの直接のセットを防ぐことができる。
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)]