Python-@property装饰器如何工作?

发布于 2021-02-02 23:23:51

我想了解内置功能的property工作原理。令我感到困惑的是,property它还可以用作装饰器,但是仅当用作内置函数时才接受参数,而不能用作装饰器。

这个例子来自文档:

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的论点是getx,setx,delx和文档字符串。

在下面的代码中property用作装饰器。它的对象是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装饰器是如何创建的?我很困惑。

关注者
0
被浏览
227
1 个回答
  • 面试哥
    面试哥 2021-02-02
    为面试而生,有面试问题,就找面试哥。

    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
    

    最后但并非最不重要的一点是,该property对象充当描述符对象,因此具有和.__get__(),可以.__set__()与.__delete__()实例属性的获取,设置和删除方法挂钩:

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

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


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看