Python の「プライベート」メソッドが実際にはプライベートではないのはなぜですか? 質問する

Python の「プライベート」メソッドが実際にはプライベートではないのはなぜですか? 質問する

Pythonでは、次のように名前の前に二重のアンダースコアを付けることで、クラス内に「プライベート」なメソッドや変数を作成することができます。__myPrivateMethod()では、これをどのように説明すればよいのでしょうか。

>>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

>>> obj.myPublicMethod()
public method

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

>>> obj._MyClass__myPrivateMethod()
this is private!!

どうしたんだ?!

よく理解できなかった人のために少し説明します。

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

パブリック メソッドとプライベート メソッドを持つクラスを作成し、インスタンス化します。

次に、そのパブリック メソッドを呼び出します。

>>> obj.myPublicMethod()
public method

次に、そのプライベートメソッドを呼び出してみます。

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

ここではすべてがうまくいっているように見えますが、呼び出すことができません。実際、これは「プライベート」です。実はそうではありません。dir()オブジェクトを実行すると、Python がすべての「プライベート」メソッドに対して魔法のように作成する新しい魔法のメソッドが表示されます。

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

この新しいメソッドの名前は常にアンダースコアで始まり、その後にクラス名、メソッド名が続きます。

>>> obj._MyClass__myPrivateMethod()
this is private!!

カプセル化ってそんなに重要なの?

いずれにせよ、Python はカプセル化をサポートしていないと聞いていたので、なぜ試してみる必要があるのでしょうか? 一体何が起こっているのでしょうか?

ベストアンサー1

名前のスクランブルは、サブクラスがスーパークラスのプライベート メソッドと属性を誤ってオーバーライドしないようにするために使用されます。外部からの意図的なアクセスを防ぐようには設計されていません。

例えば:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

もちろん、2 つの異なるクラスに同じ名前が付けられている場合は、機能しません。

おすすめ記事