18

質問は、技術的な背景についてではなく、どのユースケースで使用するのが好ましいかについて言及しています。

Python では、プロパティ記述子、またはマジック メソッドを介して属性へのアクセスを制御できます。どのユースケースで最もpythonicですか? それらはすべて同じ効果があるようです (以下の例を参照)。

私は次のような答えを探しています:

  • プロパティ: …の場合に使用する必要があります</li>
  • Descriptor : …の場合、プロパティの代わりに使用する必要があります。
  • 魔法の方法: …の場合にのみ使用してください。

ユース ケースは、メソッドで設定できない可能性がある属性です__init__。たとえば、オブジェクトがまだデータベースに存在していないが、後で存在する場合などです。属性にアクセスするたびに、設定して返すように試行する必要があります。

Python シェルで Copy&Paste を使用する例として、2 回目に要求されたときにのみ属性を表示するクラスがあります。それで、どちらが最善の方法ですか、またはそれらのいずれかが望ましいさまざまな状況がありますか? 実装方法は次の 3 つです。

プロパティ付き::

class ContactBook(object):
    intents = 0

    def __init__(self):
        self.__first_person = None

    def get_first_person(self):
        ContactBook.intents += 1
        if self.__first_person is None:
            if ContactBook.intents > 1:
                value = 'Mr. First'
                self.__first_person = value
            else:
                return None
        return self.__first_person

    def set_first_person(self, value):
        self.__first_person = value

    first_person = property(get_first_person, set_first_person)

__getattribute__::

class ContactBook(object):
    intents = 0

    def __init__(self):
        self.first_person = None

    def __getattribute__(self, name):
        if name == 'first_person' \
                and object.__getattribute__(self, name) is None:
            ContactBook.intents += 1
            if ContactBook.intents > 1:
                value = 'Mr. First'
                self.first_person = value
            else:
                value = None
        else:
            value = object.__getattribute__(self, name)
        return value

記述子::

class FirstPerson(object):
    def __init__(self, value=None):
        self.value = None

    def __get__(self, instance, owner):
        if self.value is None:
            ContactBook.intents += 1
            if ContactBook.intents > 1:
                self.value = 'Mr. First'
            else:
                return None
        return self.value


class ContactBook(object):
    intents = 0
    first_person = FirstPerson()

それぞれに次の動作があります::

book = ContactBook()
print(book.first_person)
# >>None
print(book.first_person)
# >>Mr. First
4

2 に答える 2

20

基本的に、できるだけ簡単なものを使用してください。大雑把に言えば、複雑さ/重い義務の順序は次のとおりです: 通常の属性、、、/property記述子。(カスタム記述子はどちらも、おそらくあまり頻繁に行う必要はないものです。)これは、いくつかの簡単な経験則につながります。__getattr____getattribute____getattribute__

  • property通常の属性が機能する場合は使用しないでください。
  • a が機能する場合は、独自の記述子を作成しないでくださいproperty
  • __getattr__a が機能する場合は使用しないでくださいproperty
  • __getattribute__ifは使用しないでください__getattr__

もう少し具体的に言えば、プロパティを使用して、1 つまたは少数の属性セットの処理をカスタマイズします。__getattr__すべての属性、または小さなセットを除くすべての属性の処理をカスタマイズするために使用します。使用__getattribute__したいと思って__getattr__いたが、うまくいかない場合に使用します。非常に複雑なことをしている場合は、独自の記述子クラスを作成してください。

property取得/設定をフックしたい属性の 1 つまたは少数のセットがある場合は、 を使用します。つまり、何が起こるかをカスタマイズするために作成した関数を秘密裏に呼び出して、 のobj.propようなものが必要です。obj.prop = 2

__getattr__実際にはすべてを個別に定義するのではなく、属性アクセスプロセス全体を全体としてカスタマイズしたいほど多くの属性に対してそれを行いたい場合に使用します。言い換えると、 、 、 などにフックする代わりに、obj.prop1にフックして、それを一般的に処理obj.prop2できるようにしたいほどたくさんあります。obj.<anything>

ただし、__getattr__実際に存在する属性に対して何が起こるかをオーバーライドすることはできません。それ以外の場合は AttributeError を発生させる属性の使用に対して、ブランケット ハンドリングをフックできるようにするだけです。を使用すると、 をいじらずに機能する通常の属性を含め、すべて__getattribute__を処理するためにフックできます。このため、使用するとかなり基本的な動作が損なわれる可能性があるため、使用を検討して十分でない場合にのみ使用してください。また、パフォーマンスに顕著な影響を与える可能性があります。たとえば、使用する必要があるかもしれません__getattribute____getattribute____getattr____getattribute__いくつかの属性を定義するクラスをラップしていて、それらの属性をカスタムの方法でラップできるようにしたい場合、状況によっては通常どおりに機能し、他の状況ではカスタムの動作が得られます。

最後に、独自の記述子を作成することは、かなり高度な作業です。 propertyは記述子であり、おそらく 95% のケースで必要になるのはこれだけです。独自のディスクリプタを作成する理由の簡単な例を次に示しますproperty。記述子を使用すると、コードの繰り返しを避けるために一般的な動作を除外できます。カスタム記述子は、たとえば、Django や SQLAlchemy などのシステムを駆動するために使用されます。そのレベルの複雑さで何かを書いていることに気付いた場合は、カスタム記述子を書く必要があるかもしれません。

あなたの例でpropertyは、最良の選択です。if name == 'somespecificname'内部で実行している場合、通常は (常にではありませんが) 赤旗です__getattribute__。特定の 1 つの名前だけを特別に処理する必要がある場合は、__getattribute__. 同様に、独自のディスクリプタを記述するの__get__は、プロパティの getter メソッドで記述できるものだけである場合は意味がありません。

于 2014-03-24T18:07:51.103 に答える
2

__getattribute__property(およびその他の記述子) が最初に機能するようにするフックであり、オブジェクトのすべての属性アクセスに対して呼び出されます。プロパティまたはカスタム記述子でさえニーズに十分でない場合は、低レベルの API と考えてください。フォールバックとして、他の方法で属性が見つからない場合に__getattr__によって呼び出されます。__getattribute__

property固定名を持つ動的属性__getattr__、より動的な性質の属性 (アルゴリズムによって値にマップされる一連の属性など) に使用します。

記述子は、任意のオブジェクトをインスタンスにバインドする必要がある場合に使用されます。たとえば、メソッド オブジェクトをより高度なものに置き換える必要がある場合。最近の例は、メソッド オブジェクトで追加の属性とメソッドをサポートするために必要なクラス ベースのデコレータ ラッピング メソッドです。一般に、スカラー属性の観点からまだ考えている場合、記述子は必要ありません。

于 2014-03-24T17:38:00.287 に答える