Python名のマングリング


109

他の言語では、より良いコードを生成するのに役立つ一般的なガイドラインは、常にすべてをできるだけ隠すことです。変数をプライベートにする必要があるか、保護する必要があるかについて疑問がある場合は、プライベート変数を使用することをお勧めします。

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

規約が1つのアンダースコアのみを使用することである場合、その根拠も知りたいです。

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

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

回答:


182

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

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の人もこのデフォルトを軽視しています-とにかくこれがデフォルトなので、不快に感じても、従うことをお勧めします。

どうしても「これは触れない!」というメッセージを送りたい場合 ユーザーにとって、通常の方法は、変数の前に1つの下線を付けることです。これは単なる慣例ですが、人々はそれを理解し、そのようなものを扱うときは二重の注意を払います:

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

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

他の目的で使用する場合は使用できますが、通常ではなく、推奨もされていません。

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

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

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

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

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

person.age = age;

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

person.setAge(age);

setAge() であること:

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プログラマーの意見では)カプセル化されます。

さて、実装の詳細について考えることなくクラスを使用できるようにクラスを作成した場合、必要に応じて問題はありません。何らかの理由でクラスの内部を見るために。ポイントは:あなたのAPIは良いはずで、残りは詳細です。

グイドはそう言った

まあ、これは物議を醸すものではありません:彼はそう言った、実際には。(「オープン着物」を探してください。)

これは文化です

はい、いくつかの理由がありますが、重大な理由はありません。これは主に、Pythonでのプログラミングの文化的な側面です。正直なところ、それも逆の可能性もありますが、そうではありません。また、逆に簡単に尋ねることもできます。なぜ一部の言語はデフォルトでプライベート属性を使用するのですか?Pythonの実践と同じ主な理由で、それはこれらの言語の文化であり、それぞれの選択肢には長所と短所があります。

この文化はすでにあるので、それに従うことをお勧めします。そうしないと__、スタックオーバーフローで質問するときに、Pythonプログラマーがコードからを削除するように指示することに苛立ちます。


1.カプセル化はクラスの不変条件を保護するためのものです。煩わしいので、外の世界から不必要な詳細を隠さない。2.「要点は、APIは適切で、残りは詳細です。」これは本当です。また、パブリック属性はAPIの一部です。また、パブリックセッターが適切な場合(クラスの不変式に関して)と適切でない場合があります。公開してはならない(不変条件違反のリスク)パブリックセッターを持つAPIは悪いAPIです。これは、とにかく各セッターの可視性について考える必要があることを意味し、「デフォルト」を持つことは意味が少なくなります。
木星

21

まず-名前マングリングとは何ですか?

名前のマングリングは、クラス定義で__any_nameor を使用するときに呼び出されます__any_name_。つまり、2つ(またはそれ以上)の先頭のアンダースコアと最大で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

Python標準ライブラリスタイルガイドであるPEP 8は現在次のように述べています(要約):

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

クラスをサブクラス化することを意図していて、サブクラスで使用したくない属性がある場合は、先頭に2つのアンダースコアを付け、末尾にアンダースコアを付けないように名前を付けることを検討してください。

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

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

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

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

クラス定義で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つのアンダースコアのみを使用することである場合、その根拠も知りたいです。

ユーザーが属性から手を離すことを意図している場合、私はアンダースコアを1つだけ使用する傾向がありますが、それは私のメンタルモデルでは、サブクラス作成者が名前にアクセスできるためです(簡単に見つけることができるため、サブクラス作成者は常に持っています)とにかく名前を壊しました)。

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


15

私はその実践がより良いコードを生み出すとは言いません。可視性モディファイアは、目前のタスクから注意をそらすだけであり、副作用として、意図したとおりにインターフェイスを使用するよう強制します。一般的に言えば、可視性を強制すると、プログラマーがドキュメントを正しく読んでいない場合、プログラマーが物事を台無しにすることを防ぎます。

はるかに優れたソリューションは、Pythonが推奨するルートです。クラスと変数は十分に文書化され、それらの動作は明確である必要があります。ソースが利用可能である必要があります。これは、コードを書くためのはるかに拡張性と信頼性の高い方法です。

Pythonでの私の戦略はこれです:

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

何よりも、何が何をするのかは明らかです。他の誰かがそれを使用する場合は、それを文書化してください。1年後に役立つようにしたい場合は、それを文書化します。

余談ですが、実際には他の言語で保護されている必要があります。クラスが後で継承され、それが何のために使用されるかを知ることはありません。外部コードで使用できない、または使用すべきでないと確信している変数のみを保護するのが最善です。


9

プライベートデータから始めて、必要に応じて公開することはできません。むしろ、オブジェクトのインターフェースを理解することから始めるべきです。つまり、世界が目にするもの(パブリックなもの)を理解することから始めて、それを実現するために必要なプライベートなものを理解する必要があります。

他の言語では、かつて公開されていたものを非公開にすることが難しくなります。つまり、変数をプライベートまたは保護にすると、多くのコードが壊れます。しかし、Pythonのプロパティではそうではありません。むしろ、内部データを並べ替えても同じインターフェースを維持できます。

_と__の違いは、Pythonは実際には後者を強制しようとすることです。もちろん、一生懸命頑張るわけではありませんが、難しくします。_を使用すると、他のプログラマに意図が何であるかを伝えるだけで、危険にさらされても無視できます。ただし、そのルールを無視すると役立つ場合があります。例としては、デバッグ、一時的なハッキング、使用方法を想定していないサードパーティのコードの操作などがあります。


6

これにはすでに良い答えがたくさんありますが、別のものを提供します。これはまた、二重下線は非公開ではない(実際には非公開である)と言い続ける人々への反応の一部でもあります。

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

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

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

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

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

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

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


4

まず、なぜデータを非表示にしたいのですか?なぜそれがそれほど重要なのですか?

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

本当に何かを使用してほしくない場合は、その前にアンダースコアを1つ追加します。それだけです... Pythonの使用者は、アンダースコアが1つあるものは常に機能するとは限らず、知らないうちに変更される可能性があることを知っています。

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

2つのアンダースコアを使用すると、クラスをサブクラス化するのが非常に悪くなり、そのように動作することもできなくなります。


2
ダブルアンダースコアがサブクラス化に不適切である理由を省略しました...これはあなたの答えを改善するでしょう。
マットジョイナー'17

2
ダブルアンダースコアは、サブクラスとの名前の衝突を防ぐためのものなので(サブクラスへの「引き渡し」の言い方として)、名前のマングリングがどのように問題を引き起こすかはわかりません。
アーロンホール

4

選ばれた答えは、プロパティがプライベート属性の必要性を取り除く方法を説明するのに良い仕事をしますが、モジュールレベルでその機能を追加してプライベートメソッドの必要性を取り除くこともできます

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


3

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

  • 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では「名前のマングリング」として知られています。あなたはこのようにこれにアクセスすることができます:

testObj2 = Test()
print testObj2._Test__a

test1

同様に、_aの場合、変数はそのクラスの内部変数として使用する必要があることを開発者に通知するだけであり、Pythonインタープリターはアクセスしても何も実行しませんが、適切な方法ではありません。

testObj3 = Test()
print testObj3._a

test2

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

testObj4 = Test()
print testObj4.a

test3

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


2

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

Javaでは、外部からアクセスできないようにするすべての変数をプライベートにしました。同時に、Pythonでは「秘密」がないため、これを達成することはできません(Pythonの原則の1つである「私たちはすべて大人です」)。したがって、二重下線は「みんな、このフィールドを直接使用しないでください」のみを意味します。同じ意味には単一のアンダースコアがあり、考慮されるクラスから継承しなければならないときに、同時に頭痛の原因にはなりません(二重のアンダースコアによって引き起こされる可能性のある問題の例にすぎません)。

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


「プライベート」には二重下線を、「保護」には単一下線を使用します。通常、人々はすべてに対して単一のアンダースコアを使用します(二重のアンダースコアは、通常はPythonスタイルに反するプライバシーを強制するのに役立ちます)。
Jonathan Sternberg、2011

1
しかし、それによって、2つのアンダースコアがプライベートに類似し、1つのアンダースコアが保護に類似したものになりませんか?「プライベート」から始めてみませんか?
ポールマンタ2011

@Paulいいえ、そうではありません。Pythonにはプライベートはありません。それを実現しようとするべきではありません。
ローマBodnarchuk、2011

@Roman概念的に言えば... 'private'の前後の引用符に注意してください。
ポールマンタ2011

1

「変数がプライベートなのか保護されているのかがわからない場合は、プライベート変数を使用することをお勧めします。」-はい、Pythonでも同じことが言えます。

ここでいくつかの回答は「規約」について述べていますが、それらの規約へのリンクは提供していません。Pythonの信頼できるガイド、PEP 8は明示的に述べています:

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.