@property 装饰器在 Python 中如何工作?

新手上路,请多包涵

我想了解内置函数 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 setx delx 参数为 getx

在下面的代码中 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.setterx.deleter 装饰器?

原文由 ashim 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 425
2 个回答

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>

那是旧对象的副本,但替换了其中一个功能。

请记住, @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 函数替换为装饰的方法。

以下序列还通过使用这些装饰器方法创建了一个完整的属性。

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

 >>> 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() 方法添加一个删除器:

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

Last but not least, the property object acts as a descriptor object , so it has .__get__() , .__set__() and .__delete__() methods to hook into实例属性的获取、设置和删除:

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

Descriptor Howto 包括 property() 类型的 纯 Python 示例实现

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

原文由 Martijn Pieters 发布,翻译遵循 CC BY-SA 4.0 许可协议

文档说它 只是创建只读属性的快捷方式。所以

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

相当于

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

原文由 J0HN 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题