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

下面的例子来自文档:

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

这一点已经被很多人澄清了,但这是我一直在寻找的一个直接点。 这是我认为从@property装饰器开始很重要的一点。 如:-

class UtilityMixin():
    @property
    def get_config(self):
        return "This is property"

函数“get_config()”的调用将像这样工作。

util = UtilityMixin()
print(util.get_config)

如果您注意到,我没有使用“()”括号来调用函数。这是我搜索@property装饰器的基本内容。这样你就可以像使用变量一样使用函数。

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

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

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