属性描述符

属性描述符

引子

在学django,flask的时候,需要定义模型类,也就是数据库的时候,我有些疑惑,为什么要这么写?

我拿出一个普通的项目内的模型类,大概是这个样子

from django.db import models


class BaseModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True
        ordering = ['-created_at']

这里暂时先可以这么理解,这个定义的类映射到数据库的表,类的属性映射到数据库的字段。

我们定义一个user用户类,并实例化一个user对象

class User:
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone
        

user = User('davecurry', 1231231321)

如果我们想在实例化这个对象的时候,规范大家,在传入name的值时让必须输入字符串类型,传入Phone的值时必须输入int类型,不然无法实例化该怎么做呢?当然我们用 @property 静态属性和 @属性.setter 可以完成这样的操作,但是如果user内的属性越来越多时,这样的做法会显得麻烦,且代码量大,这时候我们需要一个更简便的方法——属性描述符

属性描述符

当一个类,实现了’____get(),__set(),__delete(),三个方法其中之一,它便被称作属性描述符。我们定义两个属性描述符,一个限制字符串,一个限制整型。在User类中让属性描述符限制类中的属性。

class CharField:
    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError('string needed')
        self.value = value

    def __delete__(self, instance):
        pass

class IntField:
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise ValueError('integer needed')


class User:
    name = CharField()
    phone = IntField()


if __name__ == '__main__':
    user = User()
    user.name = 'Davie'  # 调用了set()方法
    print(user.name)  # 调用了get()放方法

结果

Davie

如果赋值成整型

if __name__ == '__main__':
    user = User()
    user.name = 123
    print(user.name)

结果报错

ValueError: string needed 

在set方法上如果传入参数类型不正确,我写了主动抛出异常的代码,所以导致报错。

实际上属性描述符分为数据描述符和非数据描述符,而且它们对于属性的查找其实是不一样的,如果我们调用user.name时,它究竟是怎么个查找顺序呢?

如果user是某个类的实例,那么user.name和它等价有着同种效果的getattr(user,‘name’)会先调用__getattribute__,如果类中定义了__getattr__方法,那么在__getattribute__抛出AttributeError的时候就会调用到__getattr__。

而对于描述符__get__的调用,则是发生在__getattribute__内部的。
具体顺序是(以user.name为例):

(1)如果“age”是出现在User或其基类的__dict__中, 且name是data descriptor(属性描述符), 那么调用其__get__方法, 否则

(2)如果“name”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘name’], 否则

(3)如果"name”出现在User或其基类的__dict__中,且不是属性描述符

(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则

(3.2)返回 __dict__['age']

(4)如果User有__getattr__方法,调用__getattr__方法,否则

(5)抛出AttributeError


借鉴:https://blog.csdn.net/paopaohll/article/details/83177744

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值