見出し画像

【Python3】プライベート(Private)変数は継承出来ないしProtectedはProtetされてないし

継承とは

この辺を見て頂いて

プライベート変数とは

「この変数はクラス内でしか使わないから、外から勝手にアクセスされないように隠しておきます」という設定をした変数のこと。クラス外からは勝手にアクセス出来なくなります。

pythonの場合、変数名の前にアンダースコアを二つ並べて

class Test:
    def __init__(self) ->None:
        self.__hoge

の用に表現するんですが、これは本来の意味のプライベート変数ではなく、「疑似」プライベート変数というちょっと厄介な代物で、内部的には「_Test__hoge」という名前に勝手に変換されて保持してるみたいなんですね。

これが継承できねぇって話です。

継承してみる

class Book:
    def __init__(self, title:str) ->None:
        self.__title = title
        
class Comic(Book):
    def __init__(self, title:str) ->None:
        super().__init__(title)
    
    def __str__(self):
        return f"漫画本です。タイトルは{self.__title}です"

本来は、ComicクラスはBookクラスを継承しているので、__titleプロパティを持っているはずです。が、Comicクラス内に作ったメソッドから__titleにアクセスしてみようとすると「_Comic__titleは存在しません」みたいなメッセージが出ます。そんな変数作ってないんですけど勝手に書き換えてます?そっかー。

よくよく考えれば”Private”変数なので継承できないんですよね。そっかー。

でもPublicにはしたくないんじゃよ

普通に

self.title

って宣言すれば継承されるんですけど、これはPublic変数と言われる状態で、つまり

book = Book("タイトルです")
print(book.title)        // "タイトルです"

book.title = "嘘ぴょん"   // title属性を書き換えてみる 
print(book.title)        // "嘘ぴょん"

で、アクセス出来ちゃうし書き換えられちゃうんですね。

基本的に、クラスに閉じ込めるタイプの情報はイミュータブル(変更できないよう)に作りたいじゃないですか、極力。勝手に書き換えられたら困るから、Publicにはしたくない。

でもタイトルなんて、ComicでもMagazineでもDictionaryでも本と名の付く全てのクラスで使いたいんで、継承できないのは不便すぎるんですよ。

そこでProtectedだ、と思うじゃん

Protected変数とは「自分と、自分を継承した子クラスからしかアクセスできない変数」です。全公開(public)と、非公開(Private)のちょうど真ん中です。やったー。

class Book:
    def __init__(self, title:str) ->None:
        self._title = title    // アンダースコアを一つにするとProtectedになる?

というわけで、変数を宣言する時のアンダースコアを一つにすれば、継承可能な非公開変数が作れるそうです。

まあ、これも「疑似Private」なんでしょうけど。

と、思ったらマジで疑似Privateでした。

クラス外からアクセス出来るじゃねぇか

普通にアクセス出来るし書き換えられるんですよ。

つまり、あくまでも見た目上の「この変数には直接アクセスすんじゃねーぞ」というプログラマの意思表示にしか使えない、ということです。意味がなーーい!(いやまあ、意思表示が出来れば十分、という考え方もありますが。本当にみんな守ってくれる??大丈夫??3年前の私みたいな人がやらかさない???)

(pythonでprotectedメソッド/変数を定義したかったらアンスコ一つにしろ、という話は結構見かけるんですけど、本当にそれでいいのだろうか)

苦肉の策として、親クラスにPublicなGetterメソッドを作る

(作りたくない)

class Book:
    def __init__(self, title:str) ->None:
        self.__title = title
        
    def get_title(self) -> str:
        return self.__title
        
class Comic(Book):
    def __init__(self, title:str) ->None:
        super().__init__(title)
    
    def __str__(self):
        return f"漫画本です。タイトルは{self.get_title()}です"

イヤー、getter作りたくなーーーーい、とは思うものの、少なくともこれなら「勝手に書き換えられる」は防げます。

ちなみにメソッドの方もアンスコ二つで始めちゃうとPrivateメソッドになっちゃって継承出来なくなっちゃうっぽいので注意が必要です。

むりくりPrivate変数にアクセスする

そこで私、ちょっと気付いちゃったんですけどね、勝手に変換が挟まっちゃうということは、

def __str__(self):
   return f"漫画本です。タイトルは{self._Book__title}です"

これでアクセスできるじゃないか、と……!

とりあえずこれで動くことは確認しました。お行儀が良いかどうかはともかく。(親クラスから継承した変数であることが見た目で分かるので、悪くないか? という気も若干しますがお行儀がいい書き方じゃないだろうなぁ)

お行儀が良いかどうかはともかく、「絶対getter作りたくないしうっかり誰かがマナー違反してProtected変数に直アクセスしてきたら困るし!!」っていう場合の抜け道にはなりそうです。

継承が絡んでくる程度にややこしいもの作り始めると、pythonはちょっと痒いところに手が届かなくなる感じがありますね……

参考:

こちらの記事が詳しくて助かりました。


この記事が気に入ったらサポートをしてみませんか?