7

Python のクラスには public/private の概念がないため、アンダースコアで始まるものは自分で作成しない限り触らないように言われます。しかし、これには、直接的または間接的に継承するすべてのクラスの完全な知識が必要ではないでしょうか? 目撃者:

class Base(object):
    def __init__(self):
        super(Base, self).__init__()
        self._foo = 0

    def foo(self):
        return self._foo + 1

class Sub(Base):
    def __init__(self):
        super(Sub, self).__init__()
        self._foo = None

Sub().foo()

期待どおり、が評価されると aTypeErrorが発生します。したがって、それが基本クラスに存在するNone + 1ことを知っておく必要があります。_fooこれを回避するには、__foo代わりに を使用できます。これは、名前を変更することで問題を解決します。これは、エレガントではないにしても、許容できる解決策のようです。しかし、Base(別のパッケージにある) というクラスから継承するとどうなるSubでしょうか? 今、祖父母の__foo私のSubオーバーライドにあります。__fooSub

これは、それぞれが使用するすべての「プライベート」オブジェクトを含む、継承チェーン全体を知る必要があることを意味します。Python が動的に型付けされるという事実は、検索する宣言がないため、これをさらに困難にします。ただし、最悪の部分は、おそらく現在はBaseから継承される可能性がありobjectますが、将来のリリースでは からの継承に切り替わることSubです。が継承されていることがわかっている場合Subは、クラスの名前を変更できますが、それは面倒です。でも未来が見えない。

これは、真のプライベート データ型が問題を防ぐケースではありませんか? Python では、将来ある時点で誰かのつま先が出現する可能性がある場合、誤って誰かのつま先を踏んでいないことをどのように確認できますか?

編集:私は明らかに主な質問を明確にしていません。私は名前マングリングと、単一アンダースコアと二重アンダースコアの違いに精通しています。問題は、現在その存在を知らないクラスと衝突する可能性があるという事実にどのように対処するかです。私の親クラス (私が書いたのではないパッケージに含まれています) がたまたま私のクラスと同じ名前のクラスから継承を開始した場合、名前マングリングでさえ役に立ちません。これを、真のプライベートメンバーが解決する(コーナー)ケースと見なすのは間違っていますが、Pythonには問題がありますか?

編集: 要求されたとおり、以下は完全な例です。

ファイルparent.py:

class Sub(object):
    def __init__(self):
        self.__foo = 12
    def foo(self):
        return self.__foo + 1
class Base(Sub):
    pass

ファイルsub.py:

import parent
class Sub(parent.Base):
    def __init__(self):
        super(Sub, self).__init__()
        self.__foo = None
Sub().foo()

祖父母のfooものは呼ばれますが、私の__fooものは使われます。

明らかに、このようなコードを自分で作成することはありませんがparent、第三者から簡単に提供される可能性があり、その詳細はいつでも変更される可能性があります。

4

6 に答える 6

7

2 つのアンダースコアで始まる(保護された名前の代わりに)プライベートな名前を使用します。

class Sub(Base):
    def __init__(self):
        super(Sub, self).__init__()
        self.__foo = None
        #    ^^

_fooまたは__fooと競合しませんBase。これは、Python が 2 つのアンダースコアを 1 つのアンダースコアとクラスの名前に置き換えるためです。次の 2 行は同等です。

class Sub(Base):
    def x(self):
        self.__foo = None # .. is the same as ..
        self._Sub__foo = None

(編集への応答:) クラス階層内の 2 つのクラスが同じ名前を持っているだけでなく、両方とも同じプロパティ名を使用しており、両方ともプライベート マングル ( __) 形式を使用している可能性は非常に小さいため、実際には安全に無視できます(これまでのところ、単一のケースについて聞いたことがありません)。

ただし、理論的には、プログラムの正確性を正式に検証するには、継承チェーン全体を最もよく知っているという点で正しいです。幸いなことに、正式な検証には通常、どのような場合でも一定のライブラリ セットが必要です。

これは、以下を含むZen of Pythonの精神に基づいています。

実用性は純粋さに勝ります。

于 2012-07-24T21:41:04.497 に答える
3
  1. 名前マングリングにはクラスが含まれているため、Base.__fooSub.__fooは異なる名前になります。これが、Python に名前マングリング機能を追加した最初の理由です。一方は_Base__foo、もう一方_Sub__fooです。

  2. 多くの人は、まさにこれらの理由から、継承 (is-a) の代わりに合成 (has-a) を使用することを好みます。

于 2012-07-24T21:41:58.520 に答える
2

これは、継承チェーン全体を知る必要があることを意味します。. .

はい、継承チェーン全体を知っている必要があります。または、直接サブクラス化するオブジェクトのドキュメントで、知っておくべきことがわかります。

サブクラス化は高度な機能であり、注意して扱う必要があります。

サブクラスでオーバーライドする必要があるものを指定するドキュメントの良い例は、スレッド クラスです。

このクラスは、別の制御スレッドで実行されるアクティビティを表します。アクティビティを指定するには、呼び出し可能なオブジェクトをコンストラクターに渡すrun()方法と、サブクラスでメソッドをオーバーライドする方法の 2 つがあります。サブクラスで他のメソッド (コンストラクターを除く) をオーバーライドする必要はありません。つまり、このクラスのメソッド__init__()とメソッドのみをオーバーライドします。run()

于 2012-07-24T22:16:55.787 に答える
2

継承チェーンの基本クラスを変更して、同じ名前のクラスからの継承をチェーンのさらに下のサブクラスと導入する頻度はどれくらいですか?

軽率ではありませんが、はい、使用しているコードを知っている必要があります。結局のところ、使用されている公開名を知っておく必要があります。Python は python であるため、祖先クラスで使用されているパブリック名を検出するには、プライベートなものを検出するのとほぼ同じ労力が必要です。

何年にもわたって Python プログラミングを行ってきましたが、実際にこれが大きな問題になることはありませんでした。インスタンス変数に名前を付けるときは、(a) 名前が他のコンテキストで使用される可能性が高いほど十分に一般的であるか、(b) 記述しているクラスが特定のコンテキストに関与する可能性が高いかどうかについて、かなり良い考えを持っている必要があります。他の未知のクラスを持つ継承階層。そのような場合、使用している名前についてもう少し慎重に考えてください。self.value属性名の良いアイデアではありませんし、Adaptor優れたクラス名のようなものでもありません。

対照的に、私は何度もアンダースコアが 2 つ付いた名前の乱用に遭遇しました。Python は Python であるため、「プライベート」な名前でさえ、クラス外で定義されたコードによってアクセスされる傾向があります。getattr外部関数に「プライベート」属性へのアクセスを許可するのは常に悪い習慣だと思うかもしれませんが、やのようなものはどうhasattrですか? 呼び出し_それらのうち、クラス独自のコードに含めることができるため、クラスは引き続きプライベート属性へのすべてのアクセスを制御しますが、手動で名前マングリングを行わないと機能しません。Python が実際に強制されたプライベート変数を持っていた場合、それらのような関数をまったく使用できませんでした。最近では、適用先の (未知の) クラスのインスタンスに「秘密の属性」を追加する必要があるデコレータ、メタクラス、または mixin などの非常に一般的なものを作成する場合に、二重下線の名前を予約する傾向があります。

そしてもちろん、標準的な動的言語の議論があります。現実には、「私のソフトウェアは機能する」という主張を正当化するには、コードを徹底的にテストする必要があります。このようなテストでは、名前が誤って衝突することによって引き起こされたバグを見逃す可能性はほとんどありません。そのテストを行っていない場合、偶発的な名前の衝突以外の方法で、さらに多くの未発見のバグが導入されることになります。

要約すると、プライベート変数の欠如は、慣用的なPythonコードでは実際にはそれほど大きな問題ではなく、真のプライベート変数を追加すると、他の方法でより頻繁に問題が発生する可能性があります。

于 2012-07-25T02:57:25.243 に答える
0

前述のように、名前マングリングを使用できます。ただし、コードを適切に文書化する場合は、単一のアンダースコア (またはなし) を使用することができます。これが問題になるほど多くのプライベート変数を使用しないでください。メソッドがプライベート変数に依存しているかどうかを説明し、変数またはメソッドの名前をクラス docstring に追加して、ユーザーに警告します。

さらに、単体テストを作成する場合は、メンバーの不変条件をチェックするテストを作成する必要があります。したがって、これらはそのような名前の衝突を表示できるはずです。

本当に「プライベート」変数が必要で、なんらかの理由で名前マングリングがニーズを満たさない場合は、プライベート状態を別のオブジェクトに分解できます。

class Foo(object):

     class Stateholder(object): pass

     def __init__(self):
         self._state = Stateholder()
         self.state.private = 1
于 2012-07-24T21:48:03.360 に答える
0

アンダースコアが 2 つあるとマングリングが発生します。単一のアンダースコアは、「しないでください」のようなものです。

すべての親クラスのすべての詳細を知る必要はありません (通常、深い継承は避けるのが最善であることに注意してください)。

于 2012-07-24T21:50:50.217 に答える