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

下面的例子来自文档:

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.")

属性的参数是getx, setx, delx和一个doc字符串。

在下面的代码中,属性被用作装饰器。它的对象是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装饰器?


当前回答

这之后:

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

等于:

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

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "I'm the 'x' property.")

等于:

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

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

等于:

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

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

    def _x_set(self, value):
        self._x = value
    x = x.setter(_x_set)

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

也就是:

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

其他回答

Property是@property装饰器后面的一个类。

你可以检查这个:

print(property) #<class 'property'>

我重写了help(property)中的示例,以显示@property语法

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

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

    @x.setter 
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

c = C()
c.x="a"
print(c.x)

函数上与property()语法相同:

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

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

正如你所看到的,我们如何使用这块土地并没有什么不同。

要回答这个问题,@property decorator是通过属性类实现的。


问题是稍微解释一下属性类。 这条线:

prop = property(g,s,d)

是初始化。我们可以这样重写:

prop = property(fget=g,fset=s,fdel=d)

fget、fset、fdel的含义:

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

下一张图片显示了我们拥有的三胞胎,来自class属性:

__get__, __set__和__delete__将被覆盖。这是Python中描述符模式的实现。

通常,描述符是具有“绑定行为”的对象属性,其属性访问已被描述符协议中的方法覆盖。

我们还可以使用属性setter、getter和delete方法将函数绑定到属性。请看下一个例子。类C的方法s2将属性设置为double。

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

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"

属性可以用两种方式声明。

为属性创建getter、setter方法,然后将这些方法作为参数传递给属性函数 使用@property装饰器。

你可以看看我写的一些关于python属性的例子。

下面是@property如何在重构代码时提供帮助的另一个例子(我只在下面总结):

假设你像这样创建了一个Money类:

class Money:
    def __init__(self, dollars, cents):
        self.dollars = dollars
        self.cents = cents

用户根据这个类创建一个库,其中他/她使用例如。

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

现在让我们假设你决定改变你的Money类,去掉美元和美分属性,而是决定只跟踪美分的总数:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

如果上面提到的用户现在尝试像以前一样运行他/她的库

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))

这将导致一个错误

AttributeError:“Money”对象没有属性“dollars”

这意味着现在每个依赖于你最初的Money类的人都必须更改所有使用美元和美分的代码行,这可能是非常痛苦的……那么,如何避免这种情况呢?通过使用@property!

就是这样:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

    # Getter and setter for dollars...
    @property
    def dollars(self):
        return self.total_cents // 100
    
    @dollars.setter
    def dollars(self, new_dollars):
        self.total_cents = 100 * new_dollars + self.cents

    # And the getter and setter for cents.
    @property
    def cents(self):
        return self.total_cents % 100
    
    @cents.setter
    def cents(self, new_cents):
        self.total_cents = 100 * self.dollars + new_cents

当我们现在从图书馆打电话

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

它将像预期的那样工作,我们不需要更改库中的任何一行代码!事实上,我们甚至不需要知道我们所依赖的库发生了变化。

setter也可以正常工作:

money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.

money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.

你也可以在抽象类中使用@property;这里我举一个最小的例子。

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

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

等于

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

第一部分很简单:

@property
def x(self): ...

def x(self): ...
x = property(x)

反过来,这是用于仅使用getter创建属性的简化语法。

下一步是使用setter和delete扩展此属性。这需要适当的方法:

@x.setter
def x(self, value): ...

返回一个新属性,它继承了旧x加上给定setter的所有内容。

X.deleter的工作方式相同。