Django的models实现分析。python元类:**type()   **

 

 

Python 中 Meta Classes详解,pythonclasses

接触过 Django 的同校都应该非凡熟识它的 O索罗德M 系统。对于 python
新手而言,那是一项大概能够被称作“黑科学和技术”的特征:只要您在models.py中不管定义几个Model的子类,Django
便得以:

  1. 获得它的字段定义,并转移成表结构
  2. 读取Meta内部类,并转化成相应的布置音信。对于尤其的Model(如abstract、proxy),还要开始展览对应的转移
  3. 为没有定义objects的Model加上三个默许的Manager

支付之余,小编也曾脑补过其背后的原理。曾经,笔者觉得是如此的:

运营时,遍历models.py中的全体属性,找到Model的子类,并对其开始展览上述的修改。
那会儿,作者还觉得本人触碰着了真理,并曾将其应用到实际生产中——为 SAE 的 KVDB
写了1个类 ORubiconM
系统。然则在落实的进度中,作者掌握感受到了那种办法的难看,而且品质并不非常满意(因为要遍历全体的概念模块)。

那便是说实际上,Django 是怎么落实的吧?

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

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

那么,就算 类
也有生命来说,对它本身的修饰就不应有由调用者来形成,而相应是天生的。

便是,python 提供了上帝的接口——那正是 Meta Classes,或然叫做“元类”。

元类 是什么?

简言之说:元类正是类的类。

率先,要有多少个概念:

python 中,一切都是对象。

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

既是,类 是 对象,对象 是 类的实例,那么——类 也理应有 类 才对。

类的类:type

在 python 中,我们能够用type检查和测试3个目的的类,如:

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其实是1个门类,全数类——包罗type本人——的类都以type。

type 简介

从 官方文书档案 中,大家能够知晓:

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

  • `type(object)`,即大家最常用的本子。
  • `type(name, bases, dict)`,1个更强硬的本子。通过点名
    类名称(`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
的黑魔法,不过以前,大家要先领会2个语法糖。

__metaclass__ 属性

有没觉着上边第三段示例有个别鬼畜呢?它勒令程序员将类的成员写成一个字典,大约是反人类。假如大家实在是要透过修改
元类 来改变 类 的表现来说,就像是就务须利用那种措施了~~大约可怕~~

幸好,python 2.2 时引进了1个语法糖:__metaclass__。

class Integer(int):

  __metaclass__ = IntMeta

后日将会等价于:

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

因此一来,我们在接纳古板类定义的同时,也能够行使元类啦。

栗子:子类净化器

需求描述

您是3个有语言洁癖的开发者,日常容不得外人讲一句脏话,在付出时也是那般。今后,你写出了3个要命棒的框架,并立时要将它公之于众了。可是,你的焦虑症又犯了:倘若你的使用者在代码中写满了脏话,如何是好?岂不是玷污了团结的清白?
即便你正是其一丧心病狂的开发者,你会如何做?

在领略元类在此之前,你或然会无从动手。可是,那个标题你能够用 元类
轻松化解——只要在类定义时过滤掉不到底的字眼就好了(百度贴吧的办事~~)。

我们的元类看起来会是这般的:

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 的 OPAJEROM 便是二个例证,有目共睹的 SQLAlchemy
也用了那种黑魔法。

其它,在一部分微型的库中,也有 元类 的身形。比如
abc(奇怪的名字~~)——那是 python 的八个内建库,用于模拟
抽象基类(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 的同桌都应有分外耳熟能详它的 OKoleosM 系统。对于 python
新手而言,那是一项大约能够被称作“黑科…

 

1.1     神奇的Django中的models

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

设置数据库models代码:

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

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

先看下有何神奇的地方:

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

此地用的是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也是三个主意,能够本着每种属性来设置,可是固然三个类有六性格格,代码就会要命的多,并且发生大批量的冗余,就如那样。

金沙注册送58 1金沙注册送58 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的都要复制3遍代码。

2.2     使用property装饰器

选拔property也是三个主意,能够针对各类属性来设置,可是一旦二个类有多少个特性,代码就会越发的多,并且产生大批量的冗余,就如这么。

金沙注册送58 3金沙注册送58 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描述符

叙述符提供了优雅、简洁、健壮和可采纳的化解方案。简单的说,三个描述符正是叁个对象,该指标表示了三个天性的值。

那就象征一旦多个Student对象有二个属性“name”,那么描述符正是另二个能够用来代表属性“name”持有值的指标。

叙述符协议中“定义了__get__”、“__set__”或”__delete__”
这几个13分方式,描述符是完成在这之中多少个或几个主意的指标。

3      Python描述符

讲述符提供了优雅、简洁、健壮和可选拔的缓解方案。简而言之,二个叙述符就是三个对象,该对象表示了八脾性质的值。

那就表示假若3个Student对象有三个性子“name”,那么描述符正是另2个力所能及用来表示属性“name”持有值的靶子。

讲述符协议中“定义了__get__”、“__set__”或”__delete__”
那一个优异情势,描述符是达成当中一个或五个艺术的指标。

何以接纳元类?

 

缘何要选用元类那种歪曲且易于失误的职能?
诚如景况下,大家并不会选择元类,99%的开发者并不会用到元类,所以一般不要考虑那一个难题。
元类主用用于创立API,贰个杰出的事例正是Django的OENVISIONM。
它让咱们得以如此定义二个类:

 

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()}
第11个参数,是3个字典等号左是
自定义的办法名,左边是已写好的点子名,这么些要注意,有参数且尚未私下认可值的情景下,要加括号;

 

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     版本一

 

金沙注册送58 5金沙注册送58 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     版本一

 

金沙注册送58 7金沙注册送58 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”),必须安装2个称谓,才能够运用。这一个与我们选取django的models时不太一样,在动用models时,不写参数也足以的。

 

3.2     版本二

不用输入变量名称。

金沙注册送58 9金沙注册送58 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时,错误提示时,不恐怕表示出此变量的称号,只好表示出3个index值。用户观察那个时,无法判定是不行变量出了难点。

 

3.2     版本二

不用输入变量名称。

金沙注册送58 11金沙注册送58 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

金沙注册送58, 

以此版本还留存一个标题,假诺三个门类有四个字段使用了NameProperty时,错误提醒时,不能代表出此变量的称呼,只好表示出八个index值。用户看到那么些时,不能看清是尤其变量出了难点。

 

4      使用元类

元类是python的中2个难关,在超越四分之二现象下都不会用到。可是在编排框架方面却是必不可贫乏的利器。

4      使用元类

元类是python的中一个难题,在超越58%景观下都不会用到。不过在编排框架方面却是必不可缺少的利器。

4.1     版本三

利用元类来控制类的作为:

金沙注册送58 13金沙注册送58 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,具备二个效果正是判定类属性是或不是为NameProperty类型,借使为那么些种类,则那个类其他实例属性storage_name值赋值为类名和属性名

 

4.1     版本三

利用元类来控制类的作为:

金沙注册送58 15金沙注册送58 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,具备3个意义就是判定类属性是否为NameProperty类型,借使为那么些体系,则这一个类型的实例属性storage_name值赋值为类名和属性名

 

4.2     版本四—模仿django的models

模仿Django的models实现:

金沙注册送58 17金沙注册送58 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实现:

金沙注册送58 19金沙注册送58 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必须有2个),就能够叫做描述符。

艺术的原型为:

  ① __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必须有三个),就能够叫做描述符。

艺术的原型为:

  ① __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地图