Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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)]

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした