Pythonのプライベートメンバは実はブライベートじゃない

Pythonのクラスでプライベートメンバを表現するときにメンバ名の先頭に__(アンダースコア*2)を利用しますが、実は全然プライベートじゃないというお話です。

Pythonの一般的なプライベートの表現

試しに、Bankクラスを作ってみようと思います。

class Bank:

    def __init__(self, name):
        self.name = name
        self.__amount = 0
  
    def deposit(self, amount):
        self.__amount += amount
    
    def withdraw(self, amount):
        self.__amount -= amount

超しょぼい銀行になりました。

__amountはプライベートメンバにしたので外からアクセスしようとすると

bank = Bank("エンジニアもどきの銀行")
print(bank.__amount)

# AttributeError: 'Bank' object has no attribute '__amount'

となってアクセスできません。

実はプライベートじゃない

ここからが本題です。
Pythonで__をつけて宣言したメンバは実はプライベートでもなんでもなく、ただ名前が変わっているだけなのです。

どんな名前に変わっているのかというと
_クラス名__メンバ名 になっています。

先ほどのBankクラスの場合、

bank = Bank("エンジニアもどきの銀行")

bank._Bank__amount += 100

print(bank._Bank__amount)
# 100

こんな感じでアクセスできてしまします。

全くプライベートではありません。

dir(bank)
['_Bank__amount', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'deposit', 'name', 'withdraw']

こんな感じで、ちゃんと属性名の一覧にも名前が変わった後の'_Bank__amount'が入っています。

ただ、名前が変わるだけでも、表面的にはプライベートメンバっぽく振る舞えるので実用上では、そこまで支障が出ないらしいです。