美文网首页
Python 属性描述符和属性查找

Python 属性描述符和属性查找

作者: vckah | 来源:发表于2018-08-10 17:02 被阅读0次

起因于 @property 装饰器,它可以将对函数的调用伪装成对属性的调用,就是不用加括号了。
假设有这么一个类:

class Test(object):
    def __init__(self, age=0):
        self.age = age
        
        ---------分割线------------
        if age < 0:
            raise ValueError("Negative value not allowed:{}".format(age))

假设我们想要将 age 做一些限制,例如非负,那么我们只能添加上面的代码。
但是如果我们使用 . 来赋值呢,这不起作用啊。所以可以这样做:

class Test(object):
    def __init__(self, age=0):
        self._age = age
    @propery
    def age(self):
        return self._age
    @propery.setter
    def age(self, age):
        if age < 0:
            raise ValueError("Negative value not allowed:{}".format(age))
    @age.deleter
    def age(self):
        raise AttributeError("Can not delete age")

但是如果有很多属性要做这样的检查呢?那么不得不写一大堆 @propery.setter ,这样代码分离性就做的不好。
所以出现了描述符?
我现在的理解就是实现了__get__(), __set__()__delete__() 三者任意一个即可。只不过只实现 __get__() 的话称为非数据描述符,意味着只可读,同时实现 __get__(), 和 __set__() 称为数据描述符,意味着可读写。
那么上面的代码就可以这样写了

class Integer(object):
    def __init__(self, name):
        # 这里也可以做一些检查,防止直接实例化
        self.name = name

    def __get__(self, instance, owner):
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Negative value not allowed")
        instance.__dict__[self.name] = value

class Test(object):
    age = Integer('test')
    
    -----------分割线------------
    def __init__(self, age):
        self.age = age

如果对 django 熟悉的话,发现这很像它的 orm。但是这样不能直接实例化了,所以可以添加上面分割线下代码。
这样可以实现一个简单的 orm

# 需求
import numbers


class Field:
    pass

class IntField(Field):
    # 数据描述符
    def __init__(self, db_column, min_value=None, max_value=None):
        self._value = None
        self.min_value = min_value
        self.max_value = max_value
        self.db_column = db_column
        if min_value is not None:
            if not isinstance(min_value, numbers.Integral):
                raise ValueError("min_value must be int")
            elif min_value < 0:
                raise ValueError("min_value must be positive int")
        if max_value is not None:
            if not isinstance(max_value, numbers.Integral):
                raise ValueError("max_value must be int")
            elif max_value < 0:
                raise ValueError("max_value must be positive int")
        if min_value is not None and max_value is not None:
            if min_value > max_value:
                raise ValueError("min_value must be smaller than max_value")

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        if value < self.min_value or value > self.max_value:
            raise ValueError("value must between min_value and max_value")
        self._value = value


class CharField(Field):
    def __init__(self, db_column, max_length=None):
        self._value = None
        self.db_column = db_column
        if max_length is None:
            raise ValueError("you must spcify max_lenth for charfiled")
        self.max_length = max_length

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError("string value need")
        if len(value) > self.max_length:
            raise ValueError("value len excess len of max_length")
        self._value = value


class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs, **kwargs):
        if name == "BaseModel":
            return super().__new__(cls, name, bases, attrs, **kwargs)
        fields = {}
        for key, value in attrs.items():
            if isinstance(value, Field):
                fields[key] = value
        attrs_meta = attrs.get("Meta", None)
        _meta = {}
        db_table = name.lower()
        if attrs_meta is not None:
            table = getattr(attrs_meta, "db_table", None)
            if table is not None:
                db_table = table
        _meta["db_table"] = db_table
        attrs["_meta"] = _meta
        attrs["fields"] = fields
        del attrs["Meta"]
        return super().__new__(cls, name, bases, attrs, **kwargs)


class BaseModel(metaclass=ModelMetaClass):
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        return super().__init__()

    def save(self):
        fields = []
        values = []
        for key, value in self.fields.items():
            db_column = value.db_column
            if db_column is None:
                db_column = key.lower()
            fields.append(db_column)
            value = getattr(self, key)
            values.append(str(value))

        sql = "insert {db_table}({fields}) value({values})".format(db_table=self._meta["db_table"],
                                                                   fields=",".join(fields), values=",".join(values))
        pass

class User(BaseModel):
    name = CharField(db_column="name", max_length=10)
    age = IntField(db_column="age", min_value=1, max_value=100)

    class Meta:
        db_table = "user"


if __name__ == "__main__":
    user = User(name="bobby", age=28)
    # user.name = "bobby"
    # user.age = 28
    user.save()

相关文章

  • Python 属性描述符和属性查找

    起因于 @property 装饰器,它可以将对函数的调用伪装成对属性的调用,就是不用加括号了。假设有这么一个类: ...

  • Python装饰器13-对象属性查找策略

    在进入使用类作为装饰器函数时,需要熟悉Python的描述符以及Python寻找属性查找策略。 使用dir查看Pyt...

  • Object.defineProperty方法

    对象的属性在创建的时候,都会带有属性描述符,分为数据描述符和存取描述符 数据描述符是具有值的属性,改属性值可能可写...

  • python描述符相关和属性查找策略

    描述符(descriptor) 描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在类的构造函数中,只...

  • 类属性和实例属性的查找顺序

    类属性:定义在类内部的变量和方法,统称为属性。 查找顺序 - MRO 查找 Python 的属性搜索算法,在 Py...

  • ★08.缓存属性和方法ID

    简介 在获取一个 属性/方法ID 的时候需要基于名称或者 属性/方法描述符 的 符号查找 , 符号查找 的代价相对...

  • python中描述符的学习

    什么是描述符 描述符是Python新式类的关键点之一,它为对象属性提供强大的API,你可以认为描述符是表示对象属性...

  • Python 描述符对象 Descriptor Objects

    Reproduce from python描述符(descriptor)、属性(Property)、函数(类)装饰...

  • 定义一个变量

    属性描述符(Property Descriptors) 我们普通的对象属性a的属性描述符(称为“数据描述符”,因为...

  • js 对象属性描述符

    ECMAScript对象中⽬前存在的属性描述符主要有两种,数据描述符(数据属性)和存取描述符(访问器属性),数据描...

网友评论

      本文标题:Python 属性描述符和属性查找

      本文链接:https://www.haomeiwen.com/subject/yoqlbftx.html