web-dev-qa-db-ja.com

Python名前マングリング

他の言語では、より良いコードを作成するのに役立つ一般的なガイドラインは、常にすべてを可能な限り非表示にすることです。変数をプライベートにするか保護するかについて疑問がある場合は、プライベートにする方が良いでしょう。

Pythonにも同じことが当てはまりますか?最初にすべてに2つの主要なアンダースコアを使用し、必要に応じて非表示(1つのアンダースコアのみ)を減らす必要がありますか?

規約でアンダースコアを1つだけ使用する場合は、その根拠も知りたいと思います。

ここに私が残したコメントがあります JBernardoの答え 。この質問をした理由と、なぜPythonが他の言語と異なるのかを知りたい理由:

私はすべてを必要に応じて公開するべきであり、それ以上ではないと考えるように訓練する言語から来ています。理由は、これにより依存関係が減り、コードをより安全に変更できるようになるからです。 Python逆に物事を行う方法-パブリックから開始し、非表示に向かって-私には奇妙です。

84
Paul Manta

疑わしい場合は、「公開」のままにしてください。つまり、属性の名前をわかりにくくするものを追加しないでください。内部値を持つクラスがある場合は、気にしないでください。書く代わりに:

_class Stack(object):

    def __init__(self):
        self.__storage = [] # Too uptight

    def Push(self, value):
        self.__storage.append(value)
_

デフォルトでこれを書いてください:

_class Stack(object):

    def __init__(self):
        self.storage = [] # No mangling

    def Push(self, value):
        self.storage.append(value)
_

これは確かに物議を醸す物議を醸す方法です。 Python初心者はそれが嫌いで、一部の古いPython人もこのデフォルトを軽んじています-しかし、とにかくデフォルトであるため、不快に感じても従うことをお勧めします。

あなたが本当に「これに触れないでください!」というメッセージを送りたい場合ユーザーにとって、通常の方法は変数の前にoneアンダースコアを付けることです。これは単なる慣習ですが、人々はそれを理解し、そのようなものを扱うときは二重に注意します:

_class Stack(object):

    def __init__(self):
        self._storage = [] # This is ok but pythonistas use it to be relaxed about it

    def Push(self, value):
        self._storage.append(value)
_

これは、プロパティ名と属性名の競合を避けるためにも役立ちます。

_ class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0
_

ダブルアンダースコアはどうですか?まあ、二重アンダースコアの魔法は主に使用されます メソッドの誤ったオーバーロードとスーパークラスの属性との名前の競合を避けるため 。何度も拡張されることが予想されるクラスを作成する場合、非常に便利です。

他の目的に使用したい場合は可能ですが、通常でも推奨されていません。

[〜#〜] edit [〜#〜]:これはなぜですか?さて、通常のPythonスタイルは、物事をプライベートにすることを強調していません-それどころか!それには多くの理由があります-それらのほとんどは物議を醸しています...それらのいくつかを見てみましょう。

Pythonにはプロパティがあります

現在、ほとんどのOO言語は反対のアプローチを使用しています。使用すべきでないものは表示されるべきではないため、属性はプライベートにする必要があります。理論的には、オブジェクト内の値を無謀に変更する人はいないため、これにより管理しやすく、結合度の低いクラスが生成されます。

ただし、それほど単純ではありません。たとえば、Javaクラスには多くの属性getterがあり、getandは、値をsetだけに設定します。単一の属性を宣言するには、7行のコードが必要です。これは、Pythonプログラマーが不必要に複雑だと言うでしょう。また、実際には、ゲッターとセッターを使用して値を変更できるため、1つのパブリックフィールドを取得するためにこの大量のコードを記述するだけです。

では、なぜこのデフォルトのプライベートポリシーに従うのでしょうか?デフォルトで属性を公開するだけです。もちろん、これはJavaで問題となります。属性に検証を追加する場合、すべてを変更する必要があるためです。

_person.age = age;
_

あなたのコードで、私たちに言ってみましょう、

_person.setAge(age);
_

setAge() being:

_public void setAge(int age) {
    if (age >= 0) {
        this.age = age;
    } else {
        this.age = 0;
    }
}
_

Java(および他の言語)では、とにかくゲッターとセッターを使用するのがデフォルトです。なぜなら、それらは書くのが面倒ですが、私がいる状況にいると気がつくと時間を節約できるからです。説明した。

ただし、Pythonにはプロパティがあるため、Pythonで行う必要はありません。このクラスがある場合:

_ class Person(object):
     def __init__(self, name, age):
         self.name = name
         self.age = age
_

年齢を検証することにした場合、コードの_person.age = age_の部分を変更する必要はありません。プロパティを追加するだけです(下図を参照)

_ class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0
_

それを行うことができ、_person.age = age_を使用する場合、なぜプライベートフィールドとゲッターとセッターを追加するのですか?

(また、 PythonはJavaではありません および ゲッターとセッターを使用することの害についてのこの記事 を参照してください。).

とにかくすべてが見える-そして隠そうとするだけで作業が複雑になる

プライベート属性がある言語でも、何らかの種類のリフレクション/イントロスペクションライブラリを介してアクセスできます。そして、人々は、フレームワークで、緊急のニーズを解決するために、それをたくさんします。問題は、イントロスペクションライブラリは、パブリック属性を使用して実行できることを行うための難しい方法であるということです。

Pythonは非常に動的な言語であるため、この負担をクラスに追加するのは逆効果です。

問題を確認することはできません-確認する必要があります

Pythonistaにとって、カプセル化はクラスの内部を見ることができないということではなく、それを見ることを避ける可能性です。つまり、カプセル化は、ユーザーが内部の詳細を気にせずに使用できるようにするコンポーネントのプロパティです。実装について気にせずにコンポーネントを使用できる場合、そのコンポーネントはカプセル化されます(Pythonプログラマーの意見)。

さて、実装の詳細を考えずに使用できるような方法でクラスを記述した場合、何らかの理由でクラスの中を見るwantなら問題ありません。重要なのは、APIは適切である必要があり、残りは詳細であるということです。

グイドはそう言った

まあ、これは議論の余地はありません: 実際にそう言った 。 (「着物を開く」を探してください。)

これは文化です

はい、いくつかの理由がありますが、重大な理由はありません。これは主にPythonでのプログラミングの文化的側面です。率直に言って、それは逆の場合もありますが、そうではありません。また、他の方法でも簡単に尋ねることができます。なぜいくつかの言語はデフォルトでプライベート属性を使用するのですか? Pythonプラクティスと同じ主な理由:これらの言語の文化であり、それぞれの選択には長所と短所があるためです。

この文化はすでにあるので、それに従うことをお勧めします。そうしないと、Stack Overflowで質問するときに、コードから____を削除するように言っているPythonプログラマーに悩まされます:)

162
brandizzi

実践がより良いコードを生み出すとは言いません。可視性修飾子は、目の前のタスクから注意をそらすだけであり、副作用として、インターフェイスが意図したとおりに使用されるように強制します。一般的に、可視性を強制すると、プログラマがドキュメントを適切に読んでいない場合にプログラマが混乱するのを防ぎます。

より良い解決策は、Pythonが推奨するルートです:クラスと変数は十分に文書化され、それらの動作は明確である必要があります。ソースは利用可能であるべきです。コード。

Python=

  1. いまいましいことを書くだけで、データがどのように保護されるべきかについて何も仮定しません。これは、問題に対する理想的なインターフェースを作成するために書くことを前提としています。
  2. おそらくが外部で使用されず、通常の「クライアントコード」インターフェイスの一部ではないものには、先頭にアンダースコアを使用します。
  3. 二重アンダースコアは、クラス内で純粋に便利なもの、または誤って公開された場合に大きな損害を引き起こすものにのみ使用してください。

何よりも、すべてが何をするのかが明確である必要があります。他の人が使用する場合は、それを文書化します。 1年後に役立つものにしたい場合は、文書化します。

副次的な注意事項として、実際には他の言語ではprotectedを使用する必要があります。クラスが後で継承される可能性があり、使用されるクラスについてはわかりません。外部コードが使用できない、または使用すべきでないと確信している変数のみを保護するのが最善です。

15
Matt Joiner

最初に-名前マングリングとは何ですか?

名前のマングリングは、クラス定義にいるときに___any_name_または___any_name__を使用する場合、つまりtwo(またはそれ以上)で呼び出されます)先行アンダースコアと最大1つの末尾アンダースコア。

_class Demo:
    __any_name = "__any_name"
    __any_other_name_ = "__any_other_name_"
_

そしていま:

_>>> [n for n in dir(Demo) if 'any' in n]
['_Demo__any_name', '_Demo__any_other_name_']
>>> Demo._Demo__any_name
'__any_name'
>>> Demo._Demo__any_other_name_
'__any_other_name_'
_

疑わしいときはどうしますか?

表向きの用途は、サブクラスがクラスが使用する属性を使用しないようにすることです。

潜在的な値は、動作をオーバーライドするサブクラスとの名前の衝突を回避し、親クラスの機能が期待どおりに機能し続けるようにすることです。ただし、Pythonドキュメンテーションの はLiskovに代用可能ではなく、この例が役立つと思われる例はありません。

欠点は、コードベースの読み取りと理解のための認知的負荷が増加することです。特に、ソースにダブルアンダースコア名があり、デバッガーにマングルされた名前が表示されるデバッグの場合はそうです。

私の個人的なアプローチは、意図的にそれを避けることです。私は非常に大きなコードベースに取り組んでいます。それのまれな用途は、親指の痛みのように突き出ており、正当化されていないようです。

あなたはそれを見たときにそれを知るようにあなたはそれを意識する必要があります。

PEP 8

PEP 8 、Python標準ライブラリスタイルガイド、現在(短縮)):

___names_の使用については、いくつかの論争があります。

クラスがサブクラス化されることを意図しており、サブクラスに使用させたくない属性がある場合は、二重アンダースコアを使用し、後続アンダースコアを使用しない名前を付けることを検討してください。

  1. マングルされた名前では単純なクラス名のみが使用されるため、サブクラスが同じクラス名と属性名の両方を選択した場合でも、名前の衝突が発生する可能性があります。

  2. 名前のマングリングは、デバッグや__getattr__()などの特定の用途に使用できますが、あまり便利ではありません。ただし、ネームマングリングアルゴリズムは十分に文書化されており、手動で簡単に実行できます。

  3. 誰もが名前のマングリングを好むわけではありません。偶発的な名前の衝突を避ける必要性と、上級の発信者による潜在的な使用とのバランスをとるようにしてください。

どのように機能しますか?

クラス定義で2つのアンダースコアを(末尾に2つのアンダースコアを付けずに)追加すると、名前がマングルされ、オブジェクトにクラス名が続くアンダースコアが追加されます。

_>>> class Foo(object):
...     __foobar = None
...     _foobaz = None
...     __fooquux__ = None
... 
>>> [name for name in dir(Foo) if 'foo' in name]
['_Foo__foobar', '__fooquux__', '_foobaz']
_

クラス定義が解析されるときにのみ、名前がマングルされることに注意してください。

_>>> Foo.__test = None
>>> Foo.__test
>>> Foo._Foo__test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute '_Foo__test'
_

また、Pythonの新しい人は、クラス定義で定義されている名前に手動でアクセスできない場合、何が起こっているのか理解できない場合があります。これは強力な理由ではありませんが、学習対象者がいる場合に考慮すべきこと。

アンダースコアが1つ?

慣例でアンダースコアを1つだけ使用する場合、その根拠も知りたいと思います。

ユーザーが属性から手を離すことを意図している場合、アンダースコアのみを使用する傾向がありますが、これは私のメンタルモデルではサブクラスが名前にアクセスできるためです(常に持っているので、とにかくマングルされた名前)。

____プレフィックスを使用するコードを確認している場合、なぜ名前マングリングを呼び出しているのか、そしてサブクラスが同じものを選択した場合に留意して、単一のアンダースコアではうまくいかないのかを尋ねますクラスとクラス属性の名前には、これにもかかわらず名前の衝突があります。

14
Aaron Hall

プライベートデータから始めて、必要に応じて公開しないでください。むしろ、オブジェクトのインターフェースを理解することから始めてください。つまり世界が見ているもの(公共のもの)を理解することから始めて、それを実現するために必要な個人的なものを理解する必要があります。

他の言語は、かつて公にされていたものを非公開にすることを困難にします。つまり変数をprivateまたはprotectedにすると、多くのコードが破損します。しかし、pythonのプロパティではこれは当てはまりません。むしろ、内部データを再配置しても同じインターフェースを維持できます。

_と__の違いは、pythonが実際に後者を強制しようとすることです。もちろん、それは本当に一生懸命にしようとはしませんが、それは難しくします。たとえば、デバッグ、一時的なハッキング、使用方法を意図していないサードパーティコードの操作などがあります。

9
Winston Ewert

これにはすでに多くの良い答えがありますが、私は別の答えを提供します。これは、二重アンダースコアはプライベートではない(実際はそうである)と言い続けている人々への部分的な対応でもあります。

Java/C#を見ると、どちらにもprivate/protected/publicがあります。これらはすべてコンパイル時の構成体です。それらはコンパイル時にのみ強制されます。 Java/C#でリフレクションを使用する場合、プライベートメソッドに簡単にアクセスできます。

現在、Pythonで関数を呼び出すたびに、本質的にリフレクションを使用しています。これらのコードはPythonでも同じです。

lst = []
lst.append(1)
getattr(lst, 'append')(1)

「ドット」構文は、後者のコードの構文糖衣です。ほとんどの場合、getattrの使用は1つの関数呼び出しだけでwithいためです。そこから悪化するだけです。

したがって、Pythonはコードをコンパイルしないため、ca n'tはプライベートのJava/C#バージョンになります。 JavaおよびC#は、実行時に関数がプライベートであるかパブリックであるかを確認できません。その情報は失われているためです(また、関数がどこから呼び出されているかはわかりません)。

今、その情報で、二重アンダースコアの名前マングリングは「プライベート」を達成するために最も理にかなっています。これで、関数が「self」インスタンスから呼び出され、「__」で始まることに気付くと、そこで名前マングリングが実行されます。単なる構文上の砂糖です。その構文糖は、データメンバーアクセスにリフレクションのみを使用する言語で「プライベート」に相当します。

免責事項:Python開発の誰からもこのようなことを言われるのを聞いたことがありません。 「プライベート」が存在しない本当の理由は文化的ですが、ほとんどのスクリプト/解釈言語にはプライベートがありません。厳密に強制可能なプライベートは、コンパイル時を除いて実用的ではありません。

6

選択した答えは、プロパティがprivate attributesの必要性を取り除く方法を説明するのに適していますが、モジュールレベルでその機能を追加してprivate methodsの必要性を取り除きます。

メソッドをモジュールレベルで関数に変換すると、サブクラスがメソッドをオーバーライドする機会がなくなります。一部の機能をモジュールレベルに移行することは、名前マングリングを使用してメソッドを非表示にしようとするよりもPythonicです。

4
Tanner_Wauchope

最初:なぜデータを非表示にしたいのですか?なぜそんなに重要なのですか?

ほとんどの場合、あなたは本当にそれをしたくありませんが、他の人がやっているのであなたはそうします。

本当に何かを使用したくない場合は、その前にoneアンダースコアを追加してください。それだけです... Pythonistasは、アンダースコアが1つあるものが毎回動作することを保証されておらず、知らないうちに変更される可能性があることを知っています。

それが私たちの生き方であり、私たちはそれで大丈夫です。

2つのアンダースコアを使用すると、クラスがサブクラスにとって非常に悪くなり、そのように動作したくない場合もあります。

4
JBernardo

次のコードスニペットは、すべての異なるケースを説明します。

  • 2つの先頭のアンダースコア(__a)
  • 単一のアンダースコア(_a)
  • アンダースコアなし(a)

    class Test:
    
    def __init__(self):
        self.__a = 'test1'
        self._a = 'test2'
        self.a = 'test3'
    
    def change_value(self,value):
        self.__a = value
        return self.__a
    

テストオブジェクトのすべての有効な属性を印刷する

testObj1 = Test()
valid_attributes = dir(testObj1)
print valid_attributes

['_Test__a', '__doc__', '__init__', '__module__', '_a', 'a', 
'change_value']

ここで、__ aの名前が_Test__aに変更され、この変数がサブクラスのいずれかによってオーバーライドされるのを防ぐことができます。この概念は、Pythonでは「Name Mangling」として知られています。これには次のようにアクセスできます。

testObj2 = Test()
print testObj2._Test__a

test1

同様に、_aの場合、変数は、そのクラスの内部変数として使用する必要があることを開発者に通知するためのものです。pythonインタープリターは、アクセスしても、しかし、それは良い習慣ではありません。

testObj3 = Test()
print testObj3._a

test2

変数は、パブリッククラス変数のようなどこからでもアクセスできます。

testObj4 = Test()
print testObj4.a

test3

答えがあなたを助けたことを願っています:)

3
Nitish Chauhan

一見、他の言語と同じであるはずです(「その他」の下ではJavaまたはC++)を意味しますが、そうではありません。

Java外部からアクセスできないはずのすべての変数をプライベートにしました。同時に、Python (Python原則が言う-「私たちはすべて大人です」)。したがって、二重アンダースコアは「男、このフィールドを直接使用しない」のみを意味します。同じ意味は単一のアンダースコアを持ちます、考慮されるクラスから継承する必要があるときに頭痛の種になることはありません(二重アンダースコアが原因で発生する可能性のある問題の例)。

そのため、「プライベート」メンバーにはデフォルトで単一の下線を使用することをお勧めします。

2

「変数をプライベートにするか保護するかについて疑問がある場合は、プライベートにすることをお勧めします。」 -はい、Pythonでも同じことが言えます。

ここでいくつかの答えは「慣習」について述べていますが、それらの慣習へのリンクを与えません。 Pythonの信頼できるガイドである PEP 8 は明示的に述べています:

疑わしい場合は、非公開を選択してください。公開属性を非公開にするよりも後で公開する方が簡単です。

パブリックとプライベートの区別、およびPython)のname manglingは、他の回答でも考慮されています。同じリンクから、

Python(一般的に不必要な作業なしで)で実際にプライベートな属性はないため、ここでは「プライベート」という用語を使用しません。

0