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


142

@propertyPythonでの使用方法に興味があります。私は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

プロパティ機能は、状況によっては役立つ場合があると思います。でもいつ?誰かが実際の例を教えてくれませんか?

ありがとう。


9
これは、私がプロパティデコレータについて見つけた最良かつ最もクリーンな説明です[ここをクリック ]
Sumudu

2
提供したリンクの最後の例の@Anubisで、c = Celsius(-500)を設定してもValueErrorがスローされなかったため、意図した結果が得られていないと思います。
Sajuuk

@Anubisに同意します。正しく実装されています:python-course.eu/python3_properties.php
anon01 '26

回答:


91

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

属性の背後に隠された複雑な計算:

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

1
私はPDB_Calculatorの例が好きです-複雑なものが抽象化され、すべてが機能し、ユーザーはシンプルさを楽しむことができます!
Adam Kurkiewicz 2013年

2
おそらく、プロの観点から、これらは非常に良い例です。しかし、初心者として、私はこの例は非常に効果がないと思います。私の悪い... :(
kmonsoor 2014年

77

簡単な使用例の1つは、読み取り専用のインスタンス属性を設定することです。Python _xで変数名の先頭にアンダースコアを1つ使用すると、通常はプライベート(内部使用)であることを意味しますが、インスタンス属性を読み取って書き込みできないようにしたい場合があります。これに使用できるよう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

6
c._xユーザーが希望する場合は、を設定できます。Pythonには実際には実際のプライベート属性はありません。

20

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


15

私が使用したことの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アプリ内にありました属性は遅延であり、遅延ではありません。


1
これには使えません@cached_propertyか?
adarsh 2014

@adarsh-面白いですね。それはどこですか?
14

私はそれを使用してきましたが、組み込みではなかったことを忘れていましたが、これで使用できます、pypi.python.org
pypi /

2
面白い。私はそれがこの回答の後に初めて公開されたと思いますが、これを読んでいる人はおそらく代わりにそれを使用するべきです。
14

10

回答とコメントを読むと、主なテーマは、回答には単純でありながら有用な例が欠けているように見えることです。ここには、@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

私にとって個人的には、ゲッター/セッターの最良の例は、後で行う必要のある変更の種類を人々に示すことですが、明らかに少し時間がかかります。
dtc

7

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

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

    return self._x

6

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

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

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

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

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

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

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

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

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


6

セッターとゲッターを使用した場合のプロパティのもう1つの優れた機能は、属性のOP =演算子(例:+ =、-=、* =など)を引き続き使用しながら、検証、アクセス制御、キャッシュなどを維持できることです。セッターとゲッターが供給します。

たとえば、クラスPersonをsetter setage(newage)とgetter getage()で作成した場合、年齢をインクリメントするには、次のように記述する必要があります。

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

しかしage、プロパティを作成すると、はるかにきれいに書くことができます:

bob.age += 1

5

あなたの質問への短い答えは、あなたの例では、利益がないということです。プロパティを含まないフォームを使用する必要があります。

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


4

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

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')

私はこれが好き。アクセサが削除機能なしで読み取り/書き込みを行っている間のみ、リーダーが読み取られるので、これを正しく読み取っていますか?どのようにデータ検証を追加しますか?私はPythonにかなり慣れていませんが、attr = reader('_attr')ラインにコールバックを追加する方法や、のような事前確認のフォームがおそらくあると思いますattr = if self.__isValid(value): reader('_attr')。提案?
Gabe Spradlin 2014

読み取り専用の変数のデータ検証について質問していたことに気付いたばかりです。しかし、明らかにこれはアクセサクラスのセッター部分にのみ適用されます。に変更attr = reader('_attr')してくださいattr = accessor('_attr')。ありがとう
Gabe Spradlin 2014

妥当性検査が必要な場合は、妥当性検査を行うための関数を追加し、無効な場合は例外(または何もしないことを含めて好きな動作)をinitに発生させます。上記を1つの可能なパターンで変更しました。バリデータはTrue | Falseを返して、セットが発生するかどうかをガイドする必要があります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.