Python で @property デコレータはどのように機能しますか? 質問する

Python で @property デコレータはどのように機能しますか? 質問する

組み込み関数がどのようにproperty動作するかを理解したいと思います。混乱しているのは、組み込み関数はpropertyデコレータとしても使用できるのに、組み込み関数として使用する場合にのみ引数を受け取り、デコレータとして使用する場合には引数を受け取らないことです。

この例は、ドキュメンテーション:

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

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

propertyの引数は、、getxおよびドキュメント文字列です。setxdelx

以下のコードではpropertyデコレータとして使用されています。そのオブジェクトは関数ですxが、上記のコードでは引数にオブジェクト関数を入れる場所がありません。

class C:
    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.setterおよびデコレータはどのように作成されますか?x.deleter

ベストアンサー1

このproperty()関数は特別な記述子オブジェクト:

>>> property()
<property object at 0x10ff07940>

追加のメソッドを持つのはこのオブジェクトです:

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

これらもデコレータとして機能します。新しいプロパティ オブジェクトを返します。

>>> property().getter(None)
<property object at 0x10ff079f0>

これは古いオブジェクトのコピーですが、関数の 1 つが置き換えられています。

覚えておいてください、@decorator構文は単なる構文糖です。構文:

@property
def foo(self): return self._foo

実際には同じ意味です

def foo(self): return self._foo
foo = property(foo)

したがって、foo関数は に置き換えられますproperty(foo)。これは、上で説明したように、特別なオブジェクトです。次に を使用すると@foo.setter()、上で示したメソッドが呼び出されproperty().setter、プロパティの新しいコピーが返されますが、今回はセッター関数が装飾されたメソッドに置き換えられます。

次のシーケンスでも、これらのデコレータ メソッドを使用して完全なプロパティを作成します。

まず、いくつかの関数を作成します。

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 

次に、propertyゲッターのみを持つオブジェクトを作成します。

>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

次に、.setter()メソッドを使用してセッターを追加します。

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

最後に、メソッドを使用して削除子を追加します.deleter()

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

最後に、このpropertyオブジェクトは記述子オブジェクト、それは.__get__().__set__()そして.__delete__()インスタンス属性の取得、設定、削除にフックするメソッド:

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

記述子Howtoには、純粋なPythonサンプル実装タイプproperty():

class Property:
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

おすすめ記事