web-dev-qa-db-ja.com

pythonの組み込み関数property()をいつ、どのように使用するか

少し構文的な砂糖を除いて、property()は何の役にも立ちません。

確かに、a.setB(2)の代わりにa.b=2を書くことができるのは良いことですが、ab = 2が単純な割り当てではないという事実を隠すことは、何らかの予期しない結果のために、トラブルのレシピのように見えますa.b=2が実際にa.b1にするなど、発生する可能性があります。または、例外が発生します。またはパフォーマンスの問題。または単に混乱している。

それを適切に使用するための具体的な例を教えてもらえますか? (問題のあるコードにパッチを適用することはカウントされません;-)

68
olamundo

Javaのようなゲッターとセッターに依存する言語では、彼らが言うこと以外は何もすることは想定も期待もされていません-x.getB()が論理属性の現在の値を返す以外は何もしなければ驚くでしょうb、またはx.setB(2)x.getB()2を返すために必要な内部作業が少しでも必要な場合を除き、何かをした場合。

ただし、この期待される動作、つまり名前がgetまたはset:むしろ、常識、社会的慣習、「スタイルガイド」、およびテストに任されています。

x.bアクセスの動作、およびx.b = 2などの割り当ては、プロパティ(Pythonを含むがこれに限定されない言語のセット)を持つ言語で正確に、たとえばJavaのgetterおよびsetterメソッドと同じ:同じ期待、同じ言語強制保証の欠如。

プロパティの最初の利点は、構文と読みやすさです。書く必要がある、例えば

x.setB(x.getB() + 1)

明らかではなく

x.b += 1

神への復geを叫びます。プロパティをサポートする言語では、クラスのユーザーにそのようなビザンチンの定型文の回転を強制する正当な理由はまったくなく、コードの可読性にまったく影響を与えません。

Python具体的には、ゲッターとセッターの代わりにプロパティ(または他の記述子)を使用するもう1つの優れた方法があります。基礎となるセッターとゲッターが不要になるようにクラスを再編成する場合もう、クラスの公開されたAPIを壊すことなく、それらのメソッドとそれらに依存するプロパティを単純に削除し、bを「論理」ではなくxのクラスの通常の「格納」属性にすることができます"計算によって取得および設定されたもの。

Pythonでは、メソッドを使用せずに直接(実行可能な場合)実行することが重要な最適化であり、プロパティを体系的に使用すると、実行可能な場合は常にこの最適化を実行できます(常に「通常の格納された属性」を公開し、アクセス時に計算が必要なもののみを公開します)および/またはメソッドとプロパティを介して設定)。

そのため、プロパティの代わりにゲッターとセッターを使用すると、ユーザーのコードの可読性に影響を与えるだけでなく、alsoマシンサイクルを無駄に浪費します(そしてそれらのサイクル中にコンピューターに送られるエネルギー;-)、再び正当な理由がない場合。

プロパティに対する唯一の引数は、たとえば「外部ユーザーは、通常、割り当ての結果として副作用を期待しません」;ただし、同じユーザー(ゲッターとセッターが普及している言語Javaなど)では、セッターを呼び出した結果として「観察可能な」「副作用」を期待できないという事実を見逃しています。 (ゲッターの場合はさらに少ないです。-)。それらは合理的な期待であり、セッターとゲッターを直接使用するかプロパティを介して使用するかは、クラス作成者があなたに任せます。重要な観察可能な副作用を持つメソッドがある場合は、notgetThissetThatという名前を付け、プロパティで使用しないでください。

プロパティが「実装を隠す」という不満は完全に不当です:ほとんどのallof OOPは、情報の隠蔽の実装に関するものです- -論理インターフェースを外部に提示し、できる限り内部で実装するクラスを作成します。ゲッターとセッターは、プロパティとまったく同じように、この目標に向けたツールです。プロパティは、それをサポートする言語でより良い仕事をしますそれら;-)。

129
Alex Martelli

アイデアは、実際に必要になるまでゲッターとセッターを書かなくても済むようにすることです。

だから、最初に書く:

class MyClass(object):
    def __init__(self):
        self.myval = 4

明らかに、myobj.myval = 5と書くことができます。

しかし、後で何か賢いことをしたいので、後でセッターが必要だと判断します。ただし、クラスを使用するすべてのコードを変更する必要はありません。したがって、セッターを@propertyデコレーターでラップすると、すべて正常に機能します。

31
Daniel Roseman

しかし、a.b = 2が単純な割り当てではないという事実を隠すことは、トラブルのレシピのように見えます

しかし、あなたはその事実を隠していません。その事実はそもそもそこにはなかった。これはpython-アセンブリではなく高水準言語です。その中の「単純な」ステートメントのいくつかは、単一のCPU命令に要約されます。ありません。

X.b = cと言うとき、おそらく考えなければならないのは、「今起きたことが何であれ、x.bはcになるはずだ」ということだけです。

14
Lee B

基本的な理由は、単に見た目が良いことです。よりPythonicです。特に図書館向け。 something.getValue()はsomething.valueよりも見栄えが悪い

Plone(かなり大きなCMS)では、値の保存、再度インデックス付けなどの多くのことを行うdocument.setTitle()を使用していました。 document.title = 'something'を実行するだけの方がいいです。とにかく多くのことが舞台裏で起こっていることを知っています。

5

あなたは正しい、それは単なる構文糖です。問題のあるコードの定義によっては、適切な使用法がない可能性があります。

アプリケーションで広く使用されているクラスFooがあると考えてください。このアプリケーションは非常に大きくなり、さらに、非常に人気のあるWebアプリだと言えます。

Fooがボトルネックを引き起こしていることがわかります。おそらく、Fooにキャッシュを追加して速度を上げることができます。プロパティを使用すると、Fooの外部でコードやテストを変更することなく、それを行うことができます。

はい、もちろんこれは問題のあるコードですが、あなたはただそれを素早く修正するためにたくさんのお金を節約しました。

Fooが数百または数千のユーザーがいるライブラリにある場合はどうなりますか? Fooの最新バージョンにアップグレードするときに、高価なリファクタリングを行うように指示する必要がなくなりました。

リリースノートには、パラグラフの移植ガイドではなく、Fooに関する項目があります。

経験豊富なPythonプログラマーはa.b=2以外にa.b==2にあまり期待していませんが、それは真実ではないかもしれないことを知っています。 。

3
John La Rooy

これが私の古い例です。 「void dt_setcharge(int atom_handle、int new_charge)」や「int dt_getcharge(int atom_handle)」などの機能を持つCライブラリをラップしました。 Pythonレベルで「atom.charge = atom.charge + 1」を実行したかった。

「プロパティ」デコレータはそれを簡単にします。何かのようなもの:

class Atom(object):
    def __init__(self, handle):
        self.handle = handle
    def _get_charge(self):
        return dt_getcharge(self.handle)
    def _set_charge(self, charge):
        dt_setcharge(self.handle, charge)
    charge = property(_get_charge, _set_charge)

10年前、このパッケージを作成したときに、__ getattr__と__setattr__を使用する必要がありましたが、それにより可能になりましたが、実装にはエラーが発生しやすくなりました。

class Atom:
    def __init__(self, handle):
        self.handle = handle
    def __getattr__(self, name):
        if name == "charge":
            return dt_getcharge(self.handle)
        raise AttributeError(name)
    def __setattr__(self, name, value):
        if name == "charge":
            dt_setcharge(self.handle, value)
        else:
            self.__dict__[name] = value
2
Andrew Dalke

ゲッターとセッターは多くの目的で必要であり、コードに対して透過的であるため非常に便利です。オブジェクトの何かをプロパティの高さとして、値をSomething.height = 10として割り当てますが、高さにゲッターとセッターがある場合、その値を割り当てるときに、minまたはmaxの検証など、プロシージャで多くのことを実行できます値。高さが変更されたためにイベントをトリガーしたり、新しい高さの値に応じて他の値を自動的に設定したり、Something.height値が割り当てられたときに発生する可能性のあるすべての値。コードでこれらを呼び出す必要はありません。プロパティ値の読み取りまたは書き込み時に自動的に実行されます。ある意味では、プロパティXが値を変更するとき、およびプロパティXの値が読み取られるときのイベントプロシージャに似ています。

0
Avenida Gez