Django的models达成分析

python元类:**type()   **

5.3     元类

元类,正是成立类的类。一般类都继承自object类,默许会创造1些方法。

元类决定了类出初阶化后有哪些特点和行事。若是大家想自定义叁个类,具备某种特殊的行为,则须求自定义元类。

  • 类也是指标,全部的类都以type的实例
  • 元类(Meta Classes)是类的类
  • __metaclass__ = Meta 是 Meta(name, bases, dict) 的语法糖
  • 能够由此重载元类的 __new__ 方法,修改定义的作为

 

 

二.壹     数据校验难题

Python即使是强类型的脚本语言,然而在概念变量时却不可能钦点变量的品种。

诸如,大家在Student类中定义二个age字段,合法值1般为包括0的正整数,不过在python中无正整数的品类,只可以协调来校验。

class Student:
    def __init__(self, name, age):
        if isinstance(name,str):
            self.name = name
        else:
            raise TypeError("Must be a string")

        if isinstance(int, age):
            self.age = age
        else:
            raise TypeError("Must be an int")

 

只是,假诺更新禧龄时就会遇见标题,不或许重用校验逻辑。

有未有简短的措施呢?

为啥使用元类?

 

怎么要动用元类那种歪曲且便于出错的功效?
1般景色下,大家并不会选用元类,9玖%的开发者并不会用到元类,所以一般不要考虑这一个标题。
元类主用用于创立API,二个独立的例证正是Django的OLX570M。
它让大家能够如此定义3个类:

 

class Person(models.Model):
  name = models.CharField(max_length=30)
  age = models.IntegerField()

 

运营上面包车型地铁代码:

guy = Person(name='bob', age='35')
print(guy.age)

重返的结果是int品种而不是IntegerField目的。那是因为models.Model使用了元类,它会将Python中定义的字段转换到数据库中的字段。
因此选择元类,Django将复杂的接口转换到简单的接口。

 

原型:type(类名,基类元组(可以为空,用于继承), 包含属性或函数的字典)

 以下三种写法都能够:

type(‘Class’,(object,),dict(hello=fun()))

type(‘Class’,(object,),{“hello”:fun()})

一、class 自定义的类名称

贰、(object,)是继承类,的元组,如果唯有一个就写那种时势(object,);八个(object,xxxx,)

三、dict(hello=fun()) 或 {“hello”:fun()}
第几个参数,是一个字典等号左是
自定义的方法名,左侧是已写好的法子名,那些要专注,有参数且没有暗中认可值的景观下,要加括号;

 

def fun():
    print('hello world!')


if __name__=="__main__":

    h = type('Hello',(object,),dict(hello=fun()))
    tc = h()
    tc.hello

 

引用:

h 也等于收到Hello类;tc
= h()实例化类;tc.hello方法,调用的实际是大家定义的fun方法。

    Hello = type('Hello',(object,),dict(hello=fun()))
    tc = Hello()
    tc.hello

 type()动态成立类后,还是能增进越来越多的办法和性情:

def mysql():
    conn = pymysql.connect(host='127.0.0.1',port=3306 ,user='root' ,passwd='q123456' ,db='amsql' )
    cur = conn.cursor()
    sql = "SELECT * FROM amt_case_interface_table"
    ret = cur.execute(sql)
    print(cur.fetchmany(3))
    #conn.commit()

    cur.close()
    conn.close()

Hello.mysql = mysql()

调用:

tc.mysql

 

Linux and
python学习交换一,二群已满.

Linux and
python学习调换三群新开,欢迎插手,一起学习.qq 三群:5632278玖四

不前进,不倒退,结束的事态是尚未的.

同台前进,与君共勉,

 

4.2     版本四—模仿django的models

模仿Django的models实现:

图片 1图片 2

import abc

class NameProperty:
    index = 0

    def __init__(self):
        self.storage_name = str(self.__class__.index)  # 使用类的变量
        self.__class__.index += 1

    def __get__(self, instance, owner):
        return getattr(instance, self.storage_name)

    def __set__(self, instance, value):
        # instance.__dict__[self.storage_name] = value
        setattr(instance, self.storage_name, value)


class Validated(abc.ABC, NameProperty):
    def __set__(self, instance, value):
        value = self.validate(instance, value)
        super().__set__(instance, value)

    @abc.abstractclassmethod
    def validate(self, instance, value):
        """return validated value or raise ValueError"""


class ChartField(Validated):
    def validate(self, instance, value):
        if not isinstance(value, str):
            raise TypeError("{} must be str".format(self.storage_name))
        return value


class IntegerField(Validated):
    def __init__(self, min_value=None):
        self.min_value = min_value

    def validate(self, instance, value):
        if not isinstance(value, int):
            raise TypeError("{} must be int".format(self.storage_name))
        if self.min_value and value < self.min_value:
            raise ValueError("{} must larger min_value".format(self.storage_name))
        return value


class EntityMeta(type):
    def __init__(cls, name, bases, attr_dict):
        super().__init__(name, bases, attr_dict)
        for key, attr in attr_dict.items():
            if isinstance(attr, Validated):
                type_name = type(attr).__name__
                attr.storage_name = "{} property {}".format(type_name, key)


class Entity(metaclass=EntityMeta):
    pass


class Student(Entity):
    name = ChartField()
    age = IntegerField(min_value=0)
    nicky_name = ChartField()

    def __init__(self, name, age, nicky_name):
        self.name = name
        self.age = age
        self.nicky_name = nicky_name

    def __str__(self):
        return self.name

s2 = Student("test", 12, "toddy")
s2.age = -1
print(s2.nicky_name)
s2.nicky_name = 4444

View Code

 

推行结果:

 

raise ValueError("{} must larger min_value".format(self.storage_name))

ValueError: IntegerField property age must larger min_value 

 

 

诸如此类,完全因袭了models的概念。

类的初叶化和延续属性赋值,都会自行调用__set__来安装并校验。

 

  • 干预创建类的长河
  • 修改类
  • 重返修改今后的类

 

元类实际上做了以下叁上面包车型地铁干活:

2.2     使用property装饰器

利用property也是二个主意,能够本着每种属性来设置,可是壹旦八个类有几性情格,代码就会拾贰分的多,并且爆发大量的冗余,仿佛那样。

图片 3图片 4

class Student:
    def __init__(self, name, age, class_no, address, phone):
        self._name = None
        self._age = None
        self.__class_no = None
        self._address = None
        self._phone = None

        self.name = name
        self.age = age
        self.class_no = class_no
        self.address = address
        self.phone = phone

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise ValueError("Must be string")
        self._name = value

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if isinstance(value, int) and value > 0:
            self._age = value
        else:
            raise ValueError("age value error")

    @property
    def address(self):
        return self._address

    @address.setter
    def address(self, value):
        if not isinstance(value, str):
            raise ValueError("Must be string")
        self._address = value

View Code

 

代码冗余太多,每一个检查str的都要复制1次代码。

 

1.1     神奇的Django中的models

大家先来看一段在Django项目中常用的代码:

设置数据库models代码:

class Students(models.Model):
    name = models.CharField()
    age = models.IntegerField()

此地有多少个神奇的地方,涉及到了python中最神秘的多少个性状。

先看下有怎样神奇的地点:

  • 字段名称name\age自动转换为了数据库中的字段名称
  • 机关校验数据类型,models.IntegerField(),会校验设置的数据类型

那里用的是python的多少个语法特性:

  • 叙述符协议
  • 元类

小编们来一步一步解开神秘面纱。

 

陆  别的案例

Django的django-rest-framework框架的serializer 也是用的那几个语法完结的。

元类是python高阶语法.
合理的选择能够减小大气重复性的代码.

七      参考资料

编号

标题

链接

1

元类

https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

2

描述符

解密 Python 的描述符(descriptor)

3

《流畅的python》

元类部分

3      Python描述符

讲述符提供了优雅、简洁、健壮和可选取的缓解方案。简单来讲,1个叙述符便是三个目标,该指标表示了三特性质的值。

那就表示倘使一个Student对象有一天性子“name”,那么描述符便是另一个力所能及用来表示属性“name”持有值的靶子。

讲述符协议中“定义了__get__”、“__set__”或”__delete__”
这个非凡措施,描述符是落成当中三个或七个办法的靶子。

相关文章