web-dev-qa-db-ja.com

Pythonオブジェクト属性-アクセスの方法論

いくつかの属性を持つクラスがあるとします。これらの属性にアクセスすることは(Pythonic-OOPで)どのように最善の意味ですか?と同じように obj.attr?または、おそらくgetアクセサーを記述しますか?そのようなものに受け入れられている命名スタイルは何ですか?

編集: 1つまたは2つの先頭の下線を使用して属性に名前を付けるベストプラクティスについて詳しく説明していただけますか?ほとんどのモジュールで、単一のアンダースコアが使用されていることがわかります。


この質問がすでに行われている場合(そして、検索しても結果が得られなかったにもかかわらず、私はそれを持っているという予感があります)、それを指摘してください-そして私はこれを閉じます。

24
Eli Bendersky

一般的に受け入れられている方法は、次のような単純な属性を使用することです。

>>> class MyClass:
...     myAttribute = 0
... 
>>> c = MyClass()
>>> c.myAttribute 
0
>>> c.myAttribute = 1
>>> c.myAttribute
1

ゲッターとセッターを作成できるようにする必要がある場合は、「pythonクラスのプロパティ」を探してください。 Ryan Tomaykoのゲッター/セッター/フクサーに関する記事 は開始(少し長いですが)

23
willurd

シングルリーディングアンダースコアとダブルリーディングアンダースコアに関しては、どちらも「プライベート」の同じ概念を示しています。つまり、属性(メソッドまたは「通常の」データ属性など)がオブジェクトのパブリックAPIの一部ではないことがわかります。直接触れることは災害を招くことだということを人々は知っているでしょう。

その上、二重先頭のアンダースコア属性(ただし、単一先頭のアンダースコア属性は除く)は、サブクラスまたは他の場所からアクセスできるようにするためにname-mangledです偶然現在のクラスの外では可能性が低くなります。あなたはまだそれらにアクセスすることができますが、それほど簡単ではありません。例えば:

_>>> class ClassA:
...     def __init__(self):
...         self._single = "Single"
...         self.__double = "Double"
...     def getSingle(self):
...         return self._single
...     def getDouble(self):
...         return self.__double
... 
>>> class ClassB(ClassA):
...     def getSingle_B(self):
...         return self._single
...     def getDouble_B(self):
...         return self.__double
... 
>>> a = ClassA()
>>> b = ClassB()
_

これで、_a._single_と_b._single_に簡単にアクセスして、ClassAによって作成された__single_属性を取得できます。

_>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')
_

ただし、aまたはbインスタンスの___double_属性に直接アクセスしようとしても機能しません。

_>>> a.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassA instance has no attribute '__double'
>>> b.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassB instance has no attribute '__double'
_

また、ClassAで定義されたメソッドは、直接取得できますが(いずれかのインスタンスで呼び出された場合)、次のようになります。

_>>> a.getDouble(), b.getDouble()
('Double', 'Double')
_

ClassBで定義されたメソッドは次のことができません。

_>>> b.getDouble_B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in getDouble_B
AttributeError: ClassB instance has no attribute '_ClassB__double'
_

そして、そのエラーの中で、何が起こっているのかについてのヒントが得られます。 ___double_属性名は、クラス内でアクセスされると、アクセスされているクラスの名前を含むように名前がマングルされますinClassAが_self.__double_にアクセスしようとすると、実際には、コンパイル時に_self._ClassA__double_のアクセスに変わります。同様にClassBの場合も同様です。 (ClassBのメソッドが___double_に割り当てられ、簡潔にするためにコードに含まれていない場合、ClassAの___double_には触れず、新しい属性を作成します。 。)この属性には他に保護機能がないため、正しい名前がわかっている場合は、直接アクセスできます。

_>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')
_

なぜこれが問題なのですか?

まあ、この属性を扱うコードの動作を継承して変更したいときはいつでも問題です。この二重アンダースコア属性に直接触れるすべてを再実装するか、クラス名を推測して名前を手動でマングルする必要があります。この二重アンダースコア属性が実際にメソッドである場合、問題はさらに悪化します。メソッドのオーバーライドまたはサブクラスでメソッドを呼び出すは、名前を手動で操作するか、メソッドを呼び出すすべてのコードを再実装することを意味します。二重下線名は使用しないでください。 getattr()を使用して、属性に動的にアクセスすることは言うまでもありません。手動でマングルする必要もあります。

一方、属性は簡単に書き換えられるだけなので、表面的な「保護」しか提供しません。手動でマングリングすることで、どのコードでも属性を取得できますが、theirコードはyourクラスの名前に依存し、あなたの側でリファクタリングする努力が必要になります。クラスのコードを記述したり名前を変更したりすると(ユーザーに表示される名前は同じままですが、Pythonの一般的な方法です)、不必要にコードが壊れてしまいます。また、「だまして」Pythonクラスに名前を付けて、名前を操作することもできます。マングルされた属性名にモジュール名が含まれていないことに注意してください。最後に、二重アンダースコア属性は、すべての属性リストと、(single)アンダースコアで始まる属性をスキップするように注意しないすべての形式のイントロスペクションに引き続き表示されます。

したがって、if二重アンダースコア名を使用し、非常に不便になる可能性があるため、非常に控えめに使用し、メソッドには使用しないでくださいまたはサブクラスが再実装したいものは何でも) 、オーバーライドまたは直接アクセス。そして、二重のアンダースコアの名前マングリングが提供することを理解してください本当の保護なし。結局、単一の先頭のアンダースコアを使用すると、同じように勝ち、(潜在的な、将来の)苦痛が少なくなります。単一の先頭の下線を使用します。

57
Thomas Wouters

編集:1つまたは2つの先行アンダースコアを使用して属性に名前を付けるベストプラクティスについて詳しく説明できますか?ほとんどのモジュールで、単一のアンダースコアが使用されていることがわかります。

単一のアンダースコアは、Pythonに特別なことを意味するものではありません。「自分が何をしているのかを知らない限り、おそらくこれにアクセスしたくない」と言うのがベストプラクティスです。ただし、二重アンダースコアを使用すると、pythonは名前を内部的にマングルし、定義されているクラスからのみアクセスできるようにします。

二重の先頭と末尾の下線は、+演算子を使用するときに呼び出される__add__などの特別な関数を示します。

詳細については、 PEP 8 、特に「命名規則」セクションを参照してください。

8

ほとんどの場合、直接アクセスするだけで、get/setメソッドは必要ないと思います。

>>> class myclass:
...     x = 'hello'
...
>>>
>>> class_inst = myclass()
>>> class_inst.x
'hello'
>>> class_inst.x = 'world'
>>> class_inst.x
'world'

ところで、dir()関数を使用して、インスタンスにアタッチされている属性/メソッドを確認できます。

>>> dir(class_inst)
['__doc__', '__module__', 'x']

2つの主要なアンダーバー「__」は、属性または関数をプライベートにするために使用されます。その他の規則については、PEP 08を参照してください。 http://www.python.org/dev/peps/pep-0008/

3
monkut

属性をプロパティに変換するのは迅速で簡単なので、Pythonは最初からアクセサーを定義する必要はありません。鮮やかなデモンストレーションについては、以下を参照してください。

中毒からの回復

Pythonでgetter/setterを実行する意味はありません。とにかく保護することはできません。プロパティを取得/設定するときに追加のコードを実行する必要がある場合は、組み込みのproperty()を参照してください(python -c'help(property ) ')

0