"@property装饰器如何工作?"

2024-04-19 02:57:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我想了解内置函数property是如何工作的。令我困惑的是property也可以用作装饰器,但它只在用作内置函数时接受参数,而在用作装饰器时不接受参数。

这个例子来自documentation

class C(object):
    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的参数是getxsetxdelx和一个doc字符串。

在下面的代码中,property用作decorator。它的对象是x函数,但是在上面的代码中,在参数中没有对象函数的位置。

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.setterx.deleter装饰器? 我很困惑。


Tags: 函数selfnone参数objectinitvaluedef
3条回答

property()函数返回一个特殊的descriptor object

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

这是旧对象的副本,但替换了其中一个函数。

记住,@decorator语法只是语法糖;语法:

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

真的和

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

因此foo函数被property(foo)替换,上面我们看到的是一个特殊的对象。然后,当您使用@foo.setter()时,您所做的是调用我在上面向您展示的property().setter方法,它将返回属性的新副本,但这次将setter函数替换为修饰方法。

下面的序列还使用那些decorator方法创建一个full-on属性。

首先,我们使用getter创建一些函数和一个property对象:

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

接下来,我们使用.setter()方法添加setter:

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

最后,我们使用.deleter()方法添加一个deleter:

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

最后但并非最不重要的是,property对象充当descriptor object,因此它有^{}^{}^{}方法来钩住实例属性获取、设置和删除:

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

描述符Howto包含一个property()类型的pure Python sample implementation

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

下面是一个如何实现@property的最小示例:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

否则word将保持方法而不是属性。

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'

Documentation says这只是创建只读属性的快捷方式。所以

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

相当于

def getx(self):
    return self._x
x = property(getx)

相关问题 更多 >