Django的models完毕分析

python元类:**type()   **

 

 

Python 中 Meta Classes详解,pythonclasses

接触过 Django 的同室都应当尤其熟悉它的 OLANDM 系统。对于 python
新手而言,这是1项差不离能够被称作“黑科技(science and technology)”的本性:只要您在models.py中不管定义一个Model的子类,Django
便足以:

  1. 收获它的字段定义,并更换来表结构
  2. 读取Meta内部类,并转化成相应的配置音信。对于特种的Model(如abstract、proxy),还要开展对应的变换
  3. 为未有定义objects的Model加上2个暗许的Manager

开发之余,作者也曾脑补过其幕后的规律。曾经,作者认为是这么的:

运维时,遍历models.py中的全部属性,找到Model的子类,并对其进行上述的改动。
当下,我还认为本身触遭逢了真理,并曾将其行使到骨子里生产中——为 SAE 的 KVDB
写了一个类 OCR-VM
系统。不过在完毕的长河中,我明显感受到了那种方法的猥琐,而且质量并不非凡(因为要遍历全部的概念模块)。

那正是说实际上,Django 是怎么落到实处的吗?

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

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

那么,假设 类
也有性命来说,对它本人的修饰就不应有由调用者来落成,而应该是天赋的。

幸而,python 提供了上帝的接口——那正是 Meta Classes,大概叫做“元类”。

元类 是什么?

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

先是,要有3个概念:

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 也是3个工厂构造函数,调用其将回来
多个type类型的实例(即 类)。
type 有七个重载版本:

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

上边多个语句等价:

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__ 属性

有没觉着上边第二段示例有些鬼畜呢?它勒令程序员将类的成员写成三个字典,差不离是反人类。借使我们实在是要通过修改
元类 来改变 类 的表现来说,如同就非得选拔那种措施了~~简直可怕~~

幸好,python 二.二 时引入了三个语法糖:__metaclass__。

class Integer(int):

  __metaclass__ = IntMeta

现今将会等价于:

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

因此1来,大家在利用古板类定义的还要,也得以选取元类啦。

栗子:子类净化器

须要描述

你是四个有语言洁癖的开发者,经常容不得别人讲一句脏话,在支付时也是如此。将来,你写出了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".

看,即便像最终2个例证中的私有属性也难逃审查,因为它们本质都是千篇1律的。

居然,你还足以对有标题标品质实行专断的修改,比如
让不文明的函数在调用时打出1银行警卫告 等等,那里就不多说了。

元类 在实际上支付中的应用

平凡费用时,元类 常用啊?

自然,Django 的 O凯雷德M 正是3个例证,无人不知的 SQLAlchemy
也用了那种黑魔法。

此外,在某些微型的库中,也有 元类 的身材。比如
abc(奇怪的名字~~)——那是 python 的3个内建库,用于模拟
抽象基类(Abstract Base Classes)。开发者能够利用 abc.abstractmethod
装饰器,将 钦赐了 __metaclass__ = abc.ABCMeta 的类的办法定义成
抽象方法,同时那么些类也成了
抽象基类,抽象基类是不可实例化的。那便完结了对 抽象基类 的模拟。

万1你也有要求动态修改类定义的急需,无妨也试试那种“黑魔法”。

小结

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

元类是python高阶语法.
合理的利用能够减掉大气重复性的代码.

1      引子

1      引子

你恐怕感兴趣的篇章:

  • Python中的Classes和Metaclasses详解

中 Meta Classes详解,pythonclasses 接触过
Django 的校友都应当13分耳熟能详它的 OEnclaveM 系统。对于 python
新手而言,那是1项差不多能够被称作“黑科…

 

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.Integer菲尔德(),会校验设置的数据类型

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

  • 讲述符协议
  • 元类

大家来一步一步解开神秘面纱。

元类实际上做了以下3上边的劳作:

2      数据校验

二      数据校验

 

二.1     数据校验难题

Python尽管是强类型的脚本语言,可是在概念变量时却无计可施钦定变量的花色。

比如说,大家在Student类中定义2个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.一     数据校验难点

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")

 

只是,假设更新年龄时就会碰着难题,不可能重用校验逻辑。

有未有简要的章程吧?

  • 干预创制类的进度
  • 修改类
  • 回来修改之后的类

2.2     使用property装饰器

利用property也是1个措施,能够本着各个属性来安装,可是尽管一个类有四个性子,代码就会足够的多,并且产生多量的冗余,就像是那样。

www.5929.com 1www.5929.com 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也是1个主意,能够针对种种属性来安装,不过一旦一个类有多个属性,代码就会十分的多,并且产生大批量的冗余,就好像这么。

www.5929.com 3www.5929.com 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描述符

叙述符提供了优雅、简洁、健壮和可选拔的消除方案。简单的讲,三个描述符便是2个目的,该目的表示了1天品质的值。

这就意味着1旦三个Student对象有1性子质“name”,那么描述符正是另叁个能够用来表示属性“name”持有值的目的。

讲述符协议中“定义了__get__”、“__set__”或”__delete__”
那几个十分措施,描述符是落成在那之中二个或三个办法的靶子。

3      Python描述符

讲述符提供了优雅、简洁、健壮和可选用的消除方案。简而言之,二个描述符正是1个目的,该对象表示了一特性质的值。

那就表示假若2个Student对象有一天个性“name”,那么描述符便是另一个能够用来表示属性“name”持有值的指标。

讲述符协议中“定义了__get__”、“__set__”或”__delete__”
那些特殊措施,描述符是达成个中1个或几个主意的目的。

何以选拔元类?

 

缘何要选用元类那种歪曲且易于失误的功用?
貌似情形下,大家并不会动用元类,9九%的开发者并不会用到元类,所以一般不要思量那么些难题。
元类主用用于创设API,叁个超人的事例就是Django的O昂科雷M。
它让我们可以那样定义1个类:

 

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,二群已满.

Linux and
python学习调换三群新开,欢迎参加,1起学习.qq 三群:5632278玖四

不前进,不倒退,甘休的情景是从未有过的.

1道前行,与君共勉,

 

3.1     版本一

 

www.5929.com 5www.5929.com 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

 

其一本子存在3个难题,正是name =
NameProperty(“sss”),必须设置七个称谓,才得以动用。那几个与我们运用django的models时不太壹致,在采纳models时,不写参数也能够的。

 

3.1     版本一

 

www.5929.com 7www.5929.com 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     版本二

不用输入变量名称。

www.5929.com 9www.5929.com 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

 

那些版本还留存1个难点,假若2个连串有四个字段使用了NameProperty时,错误提示时,不恐怕代表出此变量的名目,只好表示出1个index值。用户观望这几个时,不恐怕断定是极度变量出了难点。

 

3.2     版本二

不用输入变量名称。

www.5929.com 11www.5929.com 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

 

这几个本子还设有3个难点,如若2个项目有多少个字段使用了NameProperty时,错误提醒时,不可能代表出此变量的名目,只可以表示出四个index值。用户观望那些时,不可能断定是丰富变量出了难题。

 

四      使用元类

元类是python的中2个难处,在多数现象下都不会用到。可是在编写框架方面却是必不可缺少的利器。

四      使用元类

元类是python的中2个困难,在大部景观下都不会用到。不过在编辑框架方面却是必不可少少的利器。

4.1     版本三

选拔元类来控制类的一举一动:

www.5929.com 13www.5929.com 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,具备1个职能正是判断类属性是不是为NameProperty类型,固然为这些体系,则这几个类别的实例属性storage_name值赋值为类名和属性名

 

4.1     版本三

采纳元类来控制类的一坐一起:

www.5929.com 15www.5929.com 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

 

语法解释:

本子3相比较版本②,最大的扭转在于Student类继承了自定义元类EntityMeta。

如若对于python面向对象编制程序有精通的话,python的具有类都继承自type,type是全体类的元类。。

在此地,大家自定义的元类EntityMeta,具备二个功效就是判断类属性是不是为NameProperty类型,假如为那么些连串,则那些类其余实例属性storage_name值赋值为类名和属性名www.5929.com ,。

 

4.2     版本四—模仿django的models

模仿Django的models实现:

www.5929.com 17www.5929.com 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实现:

www.5929.com 19www.5929.com 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      原掌握释

伍.壹     属性读取顺序

由此实例读取属性时,常常重临的是实例中定义的习性。读取顺序如下:          

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

先记住这么些顺序,前面精晓描述供给。属性描述符都以概念在类中的,而不是在指标中。

伍.壹     属性读取顺序

透超过实际例读取属性时,平时再次回到的是实例中定义的习性。读取顺序如下:          

  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__
中的三个或多个(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     元类

元类,正是成立类的类。1般类都一连自object类,暗许会成立1些主意。

元类决定了类出初阶化后有怎么着特征和行为。倘若大家想自定义一个类,具备某种特殊的行事,则需求自定义元类。

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

 

陆  其他案例

Django的django-rest-framework框架的serializer 也是用的这些语法完结的。

陆  其余案例

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》

元类部分

Leave a Comment.