web-dev-qa-db-ja.com

Pythonでプロパティ機能を使用する方法についての実際の例は?

Pythonで@propertyを使用する方法に興味があります。私はpythonドキュメントを読みましたが、そこにある例は私の意見では単なるおもちゃのコードです:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

プロパティデコレータで満たされた_xをラップすることで得られるメリットはわかりません。なぜ次のように実装しないのですか:

class C(object):
    def __init__(self):
        self.x = None

プロパティ機能は、状況によっては役立つと思います。でもいつ?誰かが私に実世界の例を教えてもらえますか?

ありがとう。

132
xiao 啸

他の例としては、設定された属性の検証/フィルタリング(境界内または許容されるように強制する)および複雑なまたは急速に変化する用語の遅延評価があります。

属性の背後にある複雑な計算:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

検証:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value
88
benosteen

単純なユースケースの1つは、読み取り専用のインスタンス属性を設定することです。変数名の先頭にアンダースコア_xがpythonであることがわかっているため、通常はprivate(internal use)ですが、時々、インスタンス属性を読み取って書き込みたくないので、このためにpropertyを使用できます。

>>> class C(object):

        def __init__(self, x):
            self._x = x

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute
74
mouad

非常に実用的な使用法については、 この記事 をご覧ください。要するに、Pythonで明示的なgetter/setterメソッドを捨てることができる方法を説明しています。ある段階でそれらが必要になった場合、シームレスな実装にpropertyを使用できるからです。

20
Eli Bendersky

私が使用したことの1つは、データベースに保存されているルックアップは遅いが変更されない値をキャッシュすることです。これは、属性が計算または他の長い操作(データベースチェック、ネットワーク通信など)を必要とする状況に一般化されます。

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

これは、特定のページビューでこの種の特定の属性を1つだけ必要とするWebアプリでしたが、基礎となるオブジェクト自体にそのような属性がいくつかある場合があります-構築時にそれらをすべて初期化するのは無駄であり、プロパティを使用すると柔軟に対応できます属性は怠zyであり、そうではありません。

15
detly

プロパティは、特定のフィールドを操作したり、ミドルウェアの計算を実行したりする方法をより詳細に制御できるフィールドの抽象化です。頭に浮かぶ使用法のほとんどは、検証と事前の初期化とアクセス制限です

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x
7
Stanislav Ageev

答えとコメントを読んで、主なテーマは、答えがシンプルでありながら有用な例を欠いているように見えることです。 @propertyデコレータの簡単な使用方法を示す非常に簡単なものをここに含めました。これは、ユーザーがさまざまな異なる単位、つまりin_feetまたはin_metresを使用して距離測定を指定および取得できるようにするクラスです。

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

使用法:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14
6
Mike

セッターとゲッターの使用に関するプロパティの別の素晴らしい機能は、検証、アクセス制御、キャッシュなどを保持しながら、属性に対してOP =演算子(+ =、-=、* =など)を引き続き使用できることです。セッターとゲッターが提供します。

たとえば、セッターsetage(newage)とゲッターgetage()を使用してクラスPersonを記述した場合、年齢を増やすには次のように記述する必要があります。

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

しかし、ageをプロパティにすると、もっときれいに書くことができます。

bob.age += 1
6
quizdog

はい、投稿された元の例では、プロパティは単にインスタンス変数「x」を持つのとまったく同じように機能します。

これはpythonプロパティに関する最良のことです。外部からは、インスタンス変数とまったく同じように機能します!これにより、クラス外部からインスタンス変数を使用できます。

つまり、最初の例では実際にインスタンス変数を使用できます。状況が変化し、実装を変更することに決め、プロパティが有用な場合、プロパティへのインターフェイスはクラス外のコードから同じままです。 インスタンス変数からプロパティへの変更は、クラス外のコードには影響しません。

他の多くの言語とプログラミングコースでは、プログラマがインスタンス変数を決して公開してはならず、代わりに「ゲッター」と「セッター」を使用して、クラスの外部からアクセスする値に、質問で引用されているような単純な場合でも使用します。

多くの言語(Javaなど)を使用するクラス外のコード

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

そして、クラスを実装するとき、最初の例とまったく同じように、多くの「ゲッター」と「セッター」があります。単純なインスタンス変数を複製します。クラスの実装が変更された場合、クラス外のすべてのコードを変更する必要があるため、これらのゲッターとセッターが必要です。ただし、pythonプロパティを使用すると、クラス外のコードをインスタンス変数と同じにすることができます。そのため、プロパティを追加したり、単純なインスタンス変数を使用する場合、クラス外のコードを変更する必要はありません。したがって、ほとんどのオブジェクト指向言語とは異なり、簡単な例ではcan 'getters'および 'settersの代わりにインスタンス変数を使用します'これは本当に必要ではありません。将来プロパティに変更する場合、クラスを使用するコードを変更する必要がないという知識で安全です。

これは、複雑な動作がある場合にのみプロパティを作成する必要があることを意味し、質問で説明されているように、単純なインスタンス変数が必要な非常に一般的な単純なケースでは、インスタンス変数を使用できます。

5
innov8

あなたの質問に対する簡単な答えは、あなたの例では利益がないということです。おそらくプロパティを含まないフォームを使用する必要があります。

プロパティが存在する理由は、コードが将来変更され、突然データを処理する必要がある場合です:値をキャッシュし、アクセスを保護し、外部リソースにクエリを実行します...データのセッターwithoutインターフェイスを変更します。そのため、データにアクセスするコード内のすべての場所を見つけて変更する必要はありません。

5
SpoonMeiser

多くの人が最初に気付かないことは、独自のプロパティのサブクラスを作成できることです。これは、読み取り専用オブジェクトの属性、または読み取りと書き込みはできるが削除はできない属性を公開するのに非常に便利だとわかりました。また、オブジェクトフィールドへの変更の追跡などの機能をラップする優れた方法です。

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')
4
user1969453