威尼斯人线上娱乐

Django的models实现分析

25 3月 , 2019  

python元类:**type()   **

 

 

Python 中 Meta Classes详解,pythonclasses

接触过 Django 的同学都应当十三分纯熟它的 OKoleosM 系统。对于 python
新手而言,那是一项差不多可以被称作“黑科学和技术”的特性:只要您在models.py中不管定义二个Model的子类,Django
便足以:

  1. 收获它的字段定义,并转移成表结构
  2. 读取Meta内部类,并转化成相应的配置消息。对于尤其的Model(如abstract、proxy),还要实行对应的变换
  3. 为没有定义objects的Model加上二个默许的Manager

付出之余,笔者也曾脑补过其背后的原理。曾经,作者认为是那样的:

运营时,遍历models.py中的全部属性,找到Model的子类,并对其举行上述的修改。
那时候,小编还认为自个儿触境遇了真理,并曾将其行使到实际生育中——为 SAE 的 KVDB
写了三个类 OPAJEROM
系统。然则在达成的进度中,笔者明显感受到了那种办法的难看,而且品质并倒霉看(因为要遍历全数的概念模块)。

那就是说实际上,Django 是怎么落到实处的吧?

自古以来我们制造东西的方法都是“自上而下”的,是用切削、分割、组合的方法来制造。然而,生命是自下而上地,自发地建造起来的,这个过程极为低廉。
——王晋康 《水星播种》

那句话揭破了生命的神奇所在:真正的性命都是由中央物质自发组成的,而非造物主流水生产线式的加工。

那么,假如 类
也有生命来说,对它本身的梳洗就不该由调用者来成功,而应当是原始的。

多亏,python 提供了上帝的接口——那便是 Meta Classes,只怕叫做“元类”。

元类 是什么?

简易说:元类正是类的类。

率先,要有2个概念:

python 中,一切都以对象。

没错,一切,包括 类 本身。

既然如此,类 是 对象,对象 是 类的实例,那么——类 也应当有 类 才对。

类的类:type

在 python 中,大家得以用type检查和测试四个对象的类,如:

print type(1) # <type 'int'>

比方对贰个类操作呢?

print type(int) # <type 'type'>

class MyClass(object): pass

print type(MyClass) # <type 'type'>

print type(type) # <type 'type'>

那表达:type其实是贰个档次,全数类——包括type自个儿——的类都是type。

type 简介

从 官方文书档案 中,大家得以精通:

和 dict 类似,type 也是一个工厂构造函数,调用其将赶回
一个type类型的实例(即 类)。
type 有多个重载版本:

  • `type(object)`,即我们最常用的本子。
  • `type(name, bases, dict)`,一个更强有力的版本。通过点名
    类名称(`name`)、父类列表(`bases`)和 属性字典(`dict`)
    动态合成二个类。

上面多个语句等价:

class Integer(int):

  name = 'my integer'

  def increase(self, num):
    return num + 1

  # -------------------

  Integer = type('Integer', (int, ), {
  'name': 'my integer',
  'increase': lambda self, num: \
          num + 1  # 很酷的写法,不是么
  })

也正是说:类的定义进程,其实是type类型实例化的进程。

只是那和修饰三个已定义的类有哪些关联吗?

理所当然有啦~既然“类的概念”正是“type类型的开始化过程”,那里边必定会调用到type的构造函数(__new__()
或 __init__())。只要我们后续 type类 并修改其
__new__函数,当中出手脚就能够啊。

接下去大家将经过三个板栗感受 python
的黑魔法,可是在此以前,大家要先掌握多个语法糖。

__metaclass__ 属性

Django的models实现分析。有没觉着下面第一段示例有个别鬼畜呢?它勒令程序员将类的分子写成三个字典,简直是反人类。假如我们真的是要经过改动
元类 来改变 类 的行为的话,就像就务须利用这种办法了~~简直可怕~~

万幸,python 2.2 时引进了贰个语法糖:__metaclass__。

class Integer(int):

  __metaclass__ = IntMeta

到现在将会等价于:

Integer = IntMeta('Integer', (int, ), {})

因此一来,大家在利用守旧类定义的还要,也能够选取元类啦。

栗子:子类净化器

供给描述

您是贰个有语言洁癖的开发者,平常容不得别人讲一句脏话,在支付时也是这么。今后,你写出了2个非常厉害的框架,并立刻要将它公之于众了。不过,你的性冷淡又犯了:如果你的使用者在代码中写满了脏话,如何做?岂不是玷污了友好的高洁?
要是你就是那几个丧心病狂的开发者,你会怎么办?

在明白元类此前,你大概会无从出手。可是,那一个难题你能够用 元类
轻松消除——只要在类定义时过滤掉不根本的单词就好了(百度贴吧的干活~~)。

咱俩的元类看起来会是那样的:

sensitive_words_list = ['asshole', 'fuck', 'shit']

def detect_sensitive_words(string):
  '''检测敏感词汇'''
  words_detected = filter(lambda word: word in string.lower(), sensitive_words_list)

  if words_detected:
    raise NameError('Sensitive words {0} detected in the string "{1}".' \
      .format(
        ', '.join(map(lambda s: '"%s"' % s, words_detected)),
        string
      )
    )

class CleanerMeta(type):

  def __new__(cls, class_name, bases, attrs):
    detect_sensitive_words(class_name) # 检查类名
    map(detect_sensitive_words, attrs.iterkeys()) # 检查属性名

    print "Well done! You are a polite coder!" # 如无异常,输出祝贺消息

    return super(CleanerMeta, cls).__new__(cls, class_name, bases, attrs)
    # 重要!这行一定不能漏!!这回调用内建的类构造器来构造类,否则定义好的类将会变成 None
现在,只需这样定义基类:

class APIBase(object):

  __metaclass__ = CleanerMeta

  # ...
那么所有 APIBase 的派生类都会接受安全审查(奸笑~~):

class ImAGoodBoy(APIBase):

  a_polite_attribute = 1

# [Output] Well done! You are a polite coder!

class FuckMyBoss(APIBase):

  pass

# [Output] NameError: Sensitive words "fuck" detected in the string "FuckMyBoss".

class PretendToBePolite(APIBase):

  def __fuck_your_asshole(self):
    pass

# [Output] NameError: Sensitive words "asshole", "fuck" detected in the string "_PretendToBePolite__fuck_your_asshole".

看,即便像最终二个例证中的私有属性也难逃审查,因为它们本质都是相同的。

依旧,你还足以对有题指标习性实行私下的修改,比如
让不文明的函数在调用时打出一行警告 等等,那里就不多说了。

元类 在实质上支出中的应用

见怪不怪成本时,元类 常用吗?

本来,Django 的 O本田CR-VM 便是一个例证,远近驰名的 SQLAlchemy
也用了这种黑魔法。

其它,在某个微型的库中,也有 元类 的人影。比如
abc(奇怪的名字~~)——那是 python 的3个内建库,用于模拟
抽象基类(Abstract Base Classes)。开发者可以利用 abc.abstractmethod
装饰器,将 钦命了 __metaclass__ = abc.ABCMeta 的类的不二法门定义成
抽象方法,同时那么些类也成了
抽象基类,抽象基类是不可实例化的。那便实现了对 抽象基类 的效仿。

设若你也有供给动态修改类定义的要求,不要紧也尝试那种“黑魔法”。

小结

  1. 类 也是 对象,全部的类都以type的实例
  2. 元类(Meta Classes)是类的类
  3. __metaclass__ = Meta 是 Meta(name, bases, dict) 的 语法糖
  4. 能够通过重载元类的 __new__ 方法,修改 类定义 的行为

元类是python高阶语法.
合理的采纳能够减弱大气重复性的代码.

1      引子

1      引子

您只怕感兴趣的篇章:

  • Python中的Classes和Metaclasses详解

中 Meta Classes详解,pythonclasses 接触过
Django 的校友都应有十分耳熟能详它的 O奥迪Q7M 系统。对于 python
新手而言,那是一项差不离能够被称作“黑科…

 

1.1     神奇的Django中的models

笔者们先来看一段在Django项目中常用的代码:

设置数据库models代码:

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

那边有多少个神奇的地点,涉及到了python中最隐私的多少个特色。

先看下有如何神奇的地点:

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

此间用的是python的三个语法特性:

  • 讲述符协议
  • 元类

咱俩来一步一步解开神秘面纱。

1.1     神奇的Django中的models

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

安装数据库models代码:

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

此处有多少个神奇的地点,涉及到了python中最隐私的多少个特点。

先看下有哪些神奇的地点:

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

这边用的是python的五个语法本性:

  • 叙述符协议
  • 元类

作者们来一步一步解开神秘面纱。

元类实际上做了以下三地点的干活:

2      数据校验

2      数据校验

 

2.1     数据校验难题

Python尽管是强类型的脚本语言,可是在概念变量时却心中无数钦命变量的项目。

譬如说,大家在Student类中定义三个age字段,合法值一般为包涵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")

 

但是,假使更新春龄时就会碰到难题,不能够重用校验逻辑。

有没有简要的法门吧?

2.1     数据校验难点

Python就算是强类型的脚本语言,可是在概念变量时却无计可施钦点变量的门类。

比如说,大家在Student类中定义三个age字段,合法值一般为包罗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")

 

唯独,假若更大年龄时就会遇见难题,无法重用校验逻辑。

有没有简短的方式吗?

  • 干预创制类的经过
  • 修改类
  • 回到修改现在的类

2.2     使用property装饰器

行使property也是贰个办法,能够针对各样属性来安装,可是一旦一个类有多个属性,代码就会要命的多,并且爆发大量的冗余,就像这么。

威尼斯人线上娱乐 1威尼斯人线上娱乐 2

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的都要复制二回代码。

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的都要复制一次代码。

 

3      Python描述符

讲述符提供了优雅、简洁、健壮和可选取的消除方案。简单的说,1个描述符正是三个指标,该对象表示了贰本性质的值。

那就表示假如多个Student对象有壹性格质“name”,那么描述符就是另叁个可见用来表示属性“name”持有值的对象。

叙述符协议中“定义了__get__”、“__set__”或”__delete__”
那些特种格局,描述符是实现其中三个或八个点子的对象。

3      Python描述符

讲述符提供了优雅、简洁、健壮和可选择的化解方案。简单的讲,三个描述符正是一个目的,该指标表示了2个属性的值。

那就象征一旦一个Student对象有1个属性“name”,那么描述符就是另一个可见用来代表属性“name”持有值的对象。

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

干什么选拔元类?

 

何以要运用元类那种歪曲且不难失误的功能?
貌似情况下,我们并不会选拔元类,99%的开发者并不会用到元类,所以一般不要考虑这一个题材。
元类主用用于创设API,二个卓绝的事例正是Django的O安德拉M。
它让大家得以那样定义一个类:

 

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学习调换1,2群已满.

Linux and
python学习调换3群新开,欢迎参加,一起学习.qq 3群:563227894

不前进,不倒退,结束的事态是没有的.

一齐前进,与君共勉,

 

3.1     版本一

 

威尼斯人线上娱乐 5威尼斯人线上娱乐 6

 1 class NameProperty:
 2     def __init__(self, name=""):
 3         self.name = name
 4 
 5     def __get__(self, instance, owner):
 6         if instance is None:
 7             return self
 8         return instance.__dict__.get(self.name)
 9 
10     def __set__(self, instance, value):
11         if not isinstance(value, str):
12             raise TypeError("name must be string")
13         instance.__dict__[self.name] = value
14         
15 
16 class Student:
17     name = NameProperty('name')
18     age = None
19     heghth = None
20     weight = None
21 
22     def __init__(self, name):
23         self.name = name
24 
25     def __str__(self):
26         return self.name
27 
28     @property
29     def age(self):
30         return self.age
31 
32     @age.setter
33     def age(self, value):
34         if not isinstance(value, int):
35             raise ValueError("must be int")
36         self.age = value
37 
38 s = Student("Stitch")
39 print(s)
40 s.name = 'name'
41 print(s.name)

View Code

 

那些版本存在二个标题,正是name =
NameProperty(“sss”),必须安装一个名称,才得以应用。这些与大家接纳django的models时不太一样,在利用models时,不写参数也得以的。

 

3.1     版本一

 

威尼斯人线上娱乐 7威尼斯人线上娱乐 8

 1 class NameProperty:
 2     def __init__(self, name=""):
 3         self.name = name
 4 
 5     def __get__(self, instance, owner):
 6         if instance is None:
 7             return self
 8         return instance.__dict__.get(self.name)
 9 
10     def __set__(self, instance, value):
11         if not isinstance(value, str):
12             raise TypeError("name must be string")
13         instance.__dict__[self.name] = value
14         
15 
16 class Student:
17     name = NameProperty('name')
18     age = None
19     heghth = None
20     weight = None
21 
22     def __init__(self, name):
23         self.name = name
24 
25     def __str__(self):
26         return self.name
27 
28     @property
29     def age(self):
30         return self.age
31 
32     @age.setter
33     def age(self, value):
34         if not isinstance(value, int):
35             raise ValueError("must be int")
36         self.age = value
37 
38 s = Student("Stitch")
39 print(s)
40 s.name = 'name'
41 print(s.name)

View Code

 

其一本子存在一个标题,正是name =
NameProperty(“sss”),必须设置二个称谓,才足以利用。那一个与大家利用django的models时不太一致,在接纳models时,不写参数也得以的。

 

3.2     版本二

不用输入变量名称。

威尼斯人线上娱乐 9威尼斯人线上娱乐 10

class NameProperty:
    index = 0

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

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

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise TypeError("name must be string")
        instance.__dict__[self.name] = value


class Student:
    name = NameProperty()
    age = None

    def __str__(self):
        return self.name

s = Student()
s.name = "www"
print(s)

s2 = Student()
s2.name = "http"
print(s2)
print(s.name)

View Code

 

其一本子还留存一个标题,若是叁个品类有五个字段使用了NameProperty时,错误提醒时,不能表示出此变量的名称,只好表示出一个index值。用户看到这几个时,不可能判定是越发变量出了难点。

 

3.2     版本二

不用输入变量名称。

威尼斯人线上娱乐 11威尼斯人线上娱乐 12

class NameProperty:
    index = 0

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

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

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise TypeError("name must be string")
        instance.__dict__[self.name] = value


class Student:
    name = NameProperty()
    age = None

    def __str__(self):
        return self.name

s = Student()
s.name = "www"
print(s)

s2 = Student()
s2.name = "http"
print(s2)
print(s.name)

View Code

 

那些版本还存在二个题材,要是二个项目有八个字段使用了NameProperty时,错误提醒时,不可能代表出此变量的称谓,只好表示出一个index值。用户观看那么些时,不可能断定是非凡变量出了难点。

 

4      使用元类

元类是python的中叁个难关,在大部情景下都不会用到。可是在编辑框架方面却是必不可匮乏的利器。

4      使用元类

元类是python的中三个难点,在超越四分之一气象下都不会用到。但是在编写制定框架方面却是必不可缺乏的利器。

4.1     版本三

威尼斯人线上娱乐,应用元类来控制类的一坐一起:

威尼斯人线上娱乐 13威尼斯人线上娱乐 14

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):
        if not isinstance(value, str):
            raise TypeError("%s must be string" % self.storage_name)
        instance.__dict__[self.storage_name] = 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, NameProperty):
                type_name = type(attr).__name__
                attr.storage_name = '{} property {}'.format(type_name, key)


class Student(metaclass=EntityMeta):
    name = NameProperty()
    age = None
    nicky_name = NameProperty()

    def __str__(self):
        return self.name

s = Student()
s.name = "www"
print(s)

s2 = Student()
s2.name = "test"
s2.nicky_name = 4444
print(s2)
print(s2.nicky_name)

View Code

 

进行输出为:

 

raise TypeError("%s must be string" % self.storage_name)

TypeError: NameProperty property nicky_name must be st

 

语法解释:

本子三相对而言版本二,最大的变迁在于Student类继承了自定义元类EntityMeta。

借使对于python面向对象编制程序有打探的话,python的具有类都继承自type,type是全数类的元类。。

在此间,我们自定义的元类EntityMeta,具备3个效果正是判断类属性是或不是为NameProperty类型,假设为那几个类别,则那些类其余实例属性storage_name值赋值为类名和属性名

 

4.1     版本三

动用元类来控制类的表现:

威尼斯人线上娱乐 15威尼斯人线上娱乐 16

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):
        if not isinstance(value, str):
            raise TypeError("%s must be string" % self.storage_name)
        instance.__dict__[self.storage_name] = 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, NameProperty):
                type_name = type(attr).__name__
                attr.storage_name = '{} property {}'.format(type_name, key)


class Student(metaclass=EntityMeta):
    name = NameProperty()
    age = None
    nicky_name = NameProperty()

    def __str__(self):
        return self.name

s = Student()
s.name = "www"
print(s)

s2 = Student()
s2.name = "test"
s2.nicky_name = 4444
print(s2)
print(s2.nicky_name)

View Code

 

实行输出为:

 

raise TypeError("%s must be string" % self.storage_name)

TypeError: NameProperty property nicky_name must be st

 

语法解释:

本子三对待版本二,最大的更动在于Student类继承了自定义元类EntityMeta。

一旦对于python面向对象编程有打探的话,python的保有类都继承自type,type是全体类的元类。。

在此处,大家自定义的元类EntityMeta,具备2个功力正是判定类属性是还是不是为NameProperty类型,假设为那几个体系,则这么些类型的实例属性storage_name值赋值为类名和属性名

 

4.2     版本四—模仿django的models

模仿Django的models实现:

威尼斯人线上娱乐 17威尼斯人线上娱乐 18

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__来安装并校验。

 

4.2     版本四—模仿django的models

模仿Django的models实现:

威尼斯人线上娱乐 19威尼斯人线上娱乐 20

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__来设置并校验。

 

5      原掌握释

5      原领会释

5.1     属性读取顺序

透超过实际例读取属性时,经常重回的是实例中定义的性质。读取顺序如下:          

  1. 实例属性
  2. 类属性
  3. 父类属性
  4. __getattr__()方法

先记住那个顺序,前面驾驭描述供给。属性描述符都是概念在类中的,而不是在指标中。

5.1     属性读取顺序

因而实例读取属性时,常常重回的是实例中定义的品质。读取顺序如下:          

  1. 实例属性
  2. 类属性
  3. 父类属性
  4. __getattr__()方法

先记住这一个顺序,前边了然描述要求。属性描述符都以概念在类中的,而不是在对象中。

5.2     描述符

某些类,只若是内部定义了章程 __get__, __set__, __delete__
中的多个或多个(set,delete必须有二个),就足以称呼描述符。

情势的原型为:

  ① __get__(self, instance, owner)

  ② __set__(self, instance, value)

  ③ __del__(self, instance)

 

讲述符只绑定到类上,在实例上不见效。

讲述的调用实质为:type(objectA).__dict__[“key”].__get__(None,
objectB),objectB为描述符,objectA为定义类。

5.2     描述符

有些类,只倘使内部定义了艺术 __get__, __set__, __delete__
中的2个或多个(set,delete必须有1个),就能够称之为描述符。

主意的原型为:

  ① __get__(self, instance, owner)

  ② __set__(self, instance, value)

  ③ __del__(self, instance)

 

叙述符只绑定到类上,在实例上不奏效。

讲述的调用实质为:type(objectA).__dict__[“key”].__get__(None,
objectB),objectB为描述符,objectA为定义类。

5.3     元类

元类,便是创制类的类。一般类都再而三自object类,暗中同意会创制一些主意。

元类决定了类出初阶化后有怎么着特色和行为。如若我们想自定义三个类,具备某种特殊的作为,则要求自定义元类。

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

 

5.3     元类

元类,正是创制类的类。一般类都继承自object类,私下认可会创立一些情势。

元类决定了类出开始化后有啥特征和作为。如若我们想自定义1个类,具备某种特殊的一坐一起,则供给自定义元类。

  • 类也是目的,全部的类都以type的实例
  • 元类(Meta Classes)是类的类
  • __metaclass__ = Meta 是 Meta(name, bases, dict) 的语法糖
  • 能够经过重载元类的 __new__ 方法,修改定义的一颦一笑

 

6  其余案例

Django的django-rest-framework框架的serializer 也是用的这几个语法达成的。

6  别的案例

Django的django-rest-framework框架的serializer 也是用的那一个语法达成的。

7      参考资料

编号

标题

链接

1

元类

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

2

描述符

解密 Python 的描述符(descriptor)

3

《流畅的python》

元类部分

7      参考资料

编号

标题

链接

1

元类

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

2

描述符

解密 Python 的描述符(descriptor)

3

《流畅的python》

元类部分


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图