Pythonには「実際の」プライベート/保護されたメソッドはありません。このアプローチは、何も隠すものではありません。 Pythonが何をするかを理解したいだけです。
class Parent(object):
def _protected(self):
pass
def __private(self):
pass
class Child(Parent):
def foo(self):
self._protected() # This works
def bar(self):
self.__private() # This doesn't work, I get a AttributeError:
# 'Child' object has no attribute '_Child__private'
それで、この動作は、「保護された」メソッドは継承されますが、「プライベート」はまったく継承されないということですか?
それとも私は何かを見逃しましたか?
Pythonにはプライバシーモデルがなく、C++、C#、またはJavaのようなアクセス修飾子はありません。本当に「保護された」属性や「プライベート」な属性はありません。
先頭に二重アンダースコアがあり、末尾に二重アンダースコアがない名前は、継承時に衝突から保護するためにmangledです。サブクラスは独自の__private()
メソッドを定義でき、これらは親クラスの同じ名前と干渉しません。そのような名前はclass privateと見なされます。それらはクラス外からアクセスできますが、偶然衝突する可能性ははるかに低いです。
マングリングは、そのような名前に追加のアンダースコアとクラス名を追加することによって行われます(名前の使用方法や存在するかどうかに関係なく)、それらに事実上名前空間を与えます。 Parent
クラスでは、___private
_識別子は(コンパイル時に)名前__Parent__private
_に置き換えられますが、Child
クラスでは識別子は__Child__private
_、クラス定義のどこでも。
以下が機能します:
_class Child(Parent):
def foo(self):
self._protected()
def bar(self):
self._Parent__private()
_
字句解析ドキュメントの Reserved classes of identifiers を参照してください:
_
__*
_
クラスプライベート名。このカテゴリの名前は、クラス定義のコンテキスト内で使用される場合、書き換えられたフォームを使用するように書き換えられ、ベースクラスと派生クラスの「プライベート」属性間の名前の衝突を回避します。
および参照される 名前に関するドキュメント :
プライベート名のマングリング:クラス定義にテキストで出現する識別子が2つ以上の下線文字で始まり、2つ以上の下線で終わらない場合、そのクラスのプライベート名と見なされます。プライベート名は、コードが生成される前に長い形式に変換されます。変換により、名前の前にクラス名が挿入され、先頭の下線が削除され、単一の下線が挿入されます。たとえば、Hamという名前のクラスで発生する識別子_
__spam
_は、__Ham__spam
_に変換されます。この変換は、識別子が使用される構文コンテキストとは無関係です。
具体的に特定の名前を使用できない、またはクラスを壊す危険性があることをクラスをサブクラス化する開発者に伝える必要がない限り、クラスプライベート名を使用しないでください。公開されたフレームワークとライブラリ以外では、この機能はほとんど使用されません。
PEP 8 Python Style Guide は、プライベート名のマングリングについて次のように述べています:
クラスがサブクラス化されることを意図しており、サブクラスに使用させたくない属性がある場合は、二重アンダースコアを使用し、後続アンダースコアを使用しない名前を付けることを検討してください。これにより、Pythonの名前マングリングアルゴリズムが呼び出され、クラスの名前が属性名に変換されます。これにより、サブクラスに同じ名前の属性が誤って含まれている場合に、属性名の衝突を回避できます。
注1:マングルされた名前では単純なクラス名のみが使用されるため、サブクラスが同じクラス名と属性名の両方を選択した場合でも、名前の衝突が発生する可能性があります。
注2:名前のマングリングは、デバッグや
__getattr__()
などの特定の用途に使用できますが、あまり便利ではありません。ただし、ネームマングリングアルゴリズムは十分に文書化されており、手動で簡単に実行できます。注3:すべての人が名前のマングリングを好むわけではありません。偶発的な名前の衝突を避ける必要性と、上級の発信者による潜在的な使用とのバランスをとるようにしてください。
ダブル__
属性は_ClassName__method_name
に変更され、_method_name
によって暗示されるセマンティックプライバシーよりもプライベートになります。
本当にしたいのであれば、技術的にはまだ取得できますが、おそらく誰もそれをするつもりはないので、コード抽象化の理由から、メソッドはその時点でプライベートである可能性があります。
class Parent(object):
def _protected(self):
pass
def __private(self):
print("Is it really private?")
class Child(Parent):
def foo(self):
self._protected()
def bar(self):
self.__private()
c = Child()
c._Parent__private()
これには、メソッドが子クラスのメソッド名と衝突しないようにするという追加の利点(または主な利点と言う人もいます)があります。
また PEP8 は
1つの先頭アンダースコアは、non-publicメソッドとインスタンス変数にのみ使用します。
サブクラスとの名前の衝突を回避するには、2つの先頭の下線を使用してPythonの名前マングリングルールを呼び出します。
Pythonはこれらの名前をクラス名と組み合わせます:
class Foo
に__a
という名前の属性がある場合、Foo.__a
からはアクセスできません。 (しつこいユーザーはFoo._Foo__a
を呼び出すことでアクセスできます。)通常、サブクラス化するように設計されたクラスの属性との名前の競合を避けるために、先頭に2つのアンダースコアを使用します。
慣例により、_such_methods
にも近づかないでください。 private
として扱う必要があるということです
データメンバーをprivateとして宣言することにより:
__private()
クラスの外からはアクセスできません
Pythonは name mangling と呼ばれる手法をサポートしています。
この機能は、2つのアンダースコアが前に付いたクラスメンバーを次のように変換します。
_className.memberName
Child()
からアクセスしたい場合は、self._Parent__private()
を使用できます。
これは古い質問ですが、私はそれに遭遇し、ニースの回避策を見つけました。
保護された関数を模倣したいが、子クラスでは簡単に関数にアクセスしたいため、親クラスでマングルされた名前を付けます。
parent_class_private_func_list = [func for func in dir(Child) if func.startswith ('_Parent__')]
for parent_private_func in parent_class_private_func_list:
setattr(self, parent_private_func.replace("_Parent__", "_Child"), getattr(self, parent_private_func))
アイデアは、親の関数名を現在のネームスペースに合わせて手動で置き換えることです。これを子クラスのinit関数に追加した後、簡単な方法で関数を呼び出すことができます。
self.__private()
知る限り、2番目のケースではPython "name mangling"を実行するため、親クラスの__privateメソッドの名前は実際には次のようになります。
_Parent__private
そして、あなたはこのフォームの子供でもそれを使用することはできません