我想了解内置函数属性是如何工作的。让我困惑的是,属性也可以用作装饰器,但它只在用作内置函数时接受参数,而在用作装饰器时不接受参数。
下面的例子来自文档:
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装饰器?
我读了这里所有的帖子,意识到我们可能需要一个现实生活中的例子。为什么要用@property?
因此,考虑一个使用身份验证系统的Flask应用程序。
在models.py中声明一个模型User:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
...
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
在这段代码中,我们通过使用@property来“隐藏”属性密码,当你试图直接访问它时,它会触发AttributeError断言,而我们使用@property。Setter设置实际的实例变量password_hash。
现在在auth/views.py中,我们可以实例化一个User:
...
@auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
...
注意用户填写注册表单时来自注册表单的属性密码。密码确认发生在EqualTo的前端(' Password ', message='密码必须匹配')(如果你想知道,但这是一个与Flask表单相关的不同主题)。
我希望这个例子对大家有用
这之后:
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
def x(self): ...
和
def x(self): ...
x = property(x)
反过来,这是用于仅使用getter创建属性的简化语法。
下一步是使用setter和delete扩展此属性。这需要适当的方法:
@x.setter
def x(self, value): ...
返回一个新属性,它继承了旧x加上给定setter的所有内容。
X.deleter的工作方式相同。
在下面,我给出了一个例子来阐明@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