我想了解内置函数属性是如何工作的。让我困惑的是,属性也可以用作装饰器,但它只在用作内置函数时接受参数,而在用作装饰器时不接受参数。
下面的例子来自文档:
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"
在下面,我给出了一个例子来阐明@property
考虑一个名为Student的类,它有两个变量:name和class_number,并且希望class_number在1到5的范围内。
现在我将解释两个错误的解决方案,最后是正确的解决方案:
下面的代码是错误的,因为它没有验证class_number(在1到5的范围内)
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = class_number
尽管经过验证,但这个解决方案也是错误的:
def validate_class_number(number):
if 1 <= number <= 5:
return number
else:
raise Exception("class number should be in the range of 1 to 5")
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = validate_class_number(class_number)
因为class_number验证只在创建类实例时检查,在创建类实例后不检查(可以将class_number更改为1到5范围之外的数字):
student1 = Student("masoud",5)
student1.class_number = 7
正确的解决方法是:
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = class_number
@property
def class_number(self):
return self._class_number
@class_number.setter
def class_number(self, class_number):
if not (1 <= class_number <= 5): raise Exception("class number should be in the range of 1 to 5")
self._class_number = class_number