1. ホーム
  2. python

[解決済み] Pythonのprivateメソッドとprotectedメソッドの継承

2023-01-08 09:38:09

質問

Pythonには「本当の」private/protectedメソッドがないことは知っています。このアプローチは何かを隠すためのものではなく、私はただ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'

つまり、'protected' メソッドは継承されるが、'private' メソッドは全く継承されないということでしょうか?

それとも何か見落としているのでしょうか?

どのように解決するのですか?

Pythonにはプライバシーモデルがありません。C++、C#、Javaのようなアクセス修飾子はありません。本当に「protected」または「private」属性はありません。

先頭のダブルアンダーコアと末尾のダブルアンダーコアがない名前は が混同されます。 を使用して、継承時の衝突を防いでいます。サブクラスは独自の __private() メソッドを定義することができ、これらは親クラス上の同じ名前と干渉することはありません。このような名前は クラスのプライベート と見なされます。これらはまだクラスの外部からアクセス可能ですが、偶然に衝突する可能性ははるかに低くなります。

マングリングは、そのような名前の前に余分なアンダースコアとクラス名(その名前がどのように使われるか、またはそれが存在するかどうかにかかわらず)を付けることによって行われ、事実上、それらに 名前空間 . で Parent クラスでは、任意の __private の識別子は (コンパイル時に) _Parent__private であるのに対し Child クラスでは、識別子は _Child__private で、クラス定義のいたるところにあります。

以下のようにするとうまくいきます。

class Child(Parent):
    def foo(self):
        self._protected()

    def bar(self):
        self._Parent__private()

参照 識別子の予約クラス を字句解析のドキュメントに追加しました。

__*

クラスのプライベートな名前。このカテゴリの名前は、クラス定義のコンテキスト内で使用される場合、ベースと派生クラスの "private" 属性間の名前の衝突を避けるために、マングルドフォームを使用するように書き直されます。

と参照される の名前に関するドキュメント :

プライベートネームの揶揄 : クラス定義にテキストで現れる識別子が、2つ以上のアンダースコアで始まり、2つ以上のアンダースコアで終わらない場合、そのクラスのプライベート名とみなされます。プライベート名は、コードが生成される前に長い形式に変換されます。この変換では、先頭のアンダースコアを削除し、アンダースコア 1 つを挿入したクラス名を名前の前に挿入します。たとえば、識別子 __spam という名前のクラスで発生する識別子は、次のように変換されます。 _Ham__spam . この変換は、識別子が使用される構文的な文脈に依存しません。

以下の場合を除いて、クラス・プライベート名を使用しないでください。 特に を使用することで、クラスをサブクラス化しようとする開発者に、特定の名前を使用することはできない、あるいはクラスを破壊するリスクがあると伝える必要がなくなるからです。公開されているフレームワークやライブラリ以外では、この機能を使用することはほとんどありません。

この機能は PEP 8 Python スタイルガイド には、プライベート名のマングリングについて次のような記述があります。

クラスがサブクラス化されることを想定しており、サブクラスに使わせたくない属性がある場合 がある場合、その名前に で命名することを検討してください。これは Pythonの名前マングリングアルゴリズムが呼び出され、そこでクラスの名前が属性名にマングリングされます。 を属性名にマングルします。これは属性名の衝突を避けるのに役立ちます。 これは、サブクラスが同じ名前の属性を含んでいる場合に、属性名の衝突を避けるのに役立ちます。 を含む場合、属性名の衝突を避けることができます。

注1: マングル化された名前では単純なクラス名のみが使用されることに注意してください。 従って、サブクラスが同じクラス名と属性名の両方を選択した場合、名前の衝突が発生する可能性があります。 の両方を選択した場合、名前の衝突が発生する可能性があります。

注意2: 名前のマングリングは、デバッグなどの特定の用途や __getattr__() のような、ある種の使い方を不便にすることがあります。しかし、名前マングリングのアルゴリズム はよく文書化されており、手動で実行するのも簡単です。

注3:誰もが名前のマングリングを好むわけではありません。偶発的な名前の衝突を避ける必要性と、高度な呼び出し元が使用する可能性とのバランスをとるようにしてください。 偶発的な名前の衝突を避けることと、上級の呼び出し元が使用する可能性のバランスを取るようにしてください。