菜单

Django的models实现分析

2019年4月7日 - 皇家赌场系统

元类实际上做了以下三方面包车型客车做事:

4.1     版本三

采纳元类来控制类的表现:

图片 1图片 2

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值赋值为类名和属性名

 

5.3     元类

元类,正是创立类的类。1般类都持续自object类,默许会创制一些措施。

元类决定了类出开端化后有哪些特点和表现。假若大家想自定义3个类,具备某种特殊的行事,则须要自定义元类。

 

Python 中 Meta Classes详解,pythonclasses

接触过 Django 的同学都应当特别熟谙它的 O奥迪Q三M 系统。对于 python
新手而言,那是壹项大概能够被称作“黑科学和技术”的特色:只要您在models.py中不管定义3个Model的子类,Django
便得以:

  1. 获得它的字段定义,并转移成表结构
  2. 读取Meta内部类,并转化成相应的安排音信。对于万分规的Model(如abstract、proxy),还要举办相应的变换
  3. 为未有定义objects的Model加上二个暗许的Manager

付出之余,笔者也曾脑补过其背后的法则。曾经,小编觉着是那样的:

运行时,遍历models.py中的全部属性,找到Model的子类,并对其展开上述的改动。
那时候,作者还以为自个儿触遇到了真理,并曾将其选拔到实在生育中——为 SAE 的 KVDB
写了三个类 O奥迪Q伍M
系统。不过在促成的进程中,作者明明感受到了那种措施的猥琐,而且品质并不完美(因为要遍历全数的定义模块)。

这正是说实际上,Django 是怎么落到实处的呢?

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

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

那正是说,借使 类
也有性命来说,对它和谐的梳洗就不应有由调用者来形成,而相应是自发的。

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

元类 是什么?

粗略说:元类正是类的类。

首先,要有贰个定义:

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

上面多少个语句等价:

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, ), {})

因而一来,大家在行使古板类定义的还要,也得以动用元类啦。

栗子:子类净化器

须求描述

你是三个有语言洁癖的开发者,平常容不得旁人讲一句脏话,在付出时也是那样。现在,你写出了三个十分的屌的框架,并立刻要将它公之于众了。可是,你的情感障碍又犯了:要是你的使用者在代码中写满了脏话,怎么做?岂不是玷污了投机的纯洁?
一经你就是其一丧心病狂的开发者,你会如何是好?

在明亮元类此前,你可能会无从动手。不过,那一个题材你能够用 元类
轻松化解——只要在类定义时过滤掉不彻底的字眼就好了(百度贴吧的工作~~)。

小编们的元类看起来会是那般的:

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奇骏M 正是多个事例,赫赫有名的 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__ 方法,修改 类定义 的行为

 

陆  其余案例

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

二      数据校验

您或者感兴趣的稿子:

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

4.2     版本四—模仿django的models

模仿Django的models实现:

图片 3图片 4

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

 

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

 

那一个版本存在3个标题,正是name =
NameProperty(“sss”),必须安装叁个名号,才能够应用。那么些与大家选取django的models时不太一样,在利用models时,不写参数也足以的。

 

元类是python高阶语法.
合理的行使能够削减大气重复性的代码.

7      参考资料

编号

标题

链接

1

元类

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

2

描述符

解密 Python 的描述符(descriptor)

3

《流畅的python》

元类部分

5.一     属性读取顺序

通超过实际例读取属性时,日常再次回到的是实例中定义的品质。读取顺序如下:          

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

先记住那个顺序,前面通晓描述要求。属性描述符都以概念在类中的,而不是在目的中。

干什么采用元类?

 

何以要运用元类那种歪曲且不难失误的作用?
相似意况下,大家并不会使用元类,9玖%的开发者并不会用到元类,所以1般不要思念那些题材。
元类主用用于创设API,多少个特出的事例正是Django的O讴歌RDXM。
它让我们得以那样定义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 自定义的类名称

2、(object,)是继承类,的元组,假设唯有一个就写那种时势(object,);多少个(object,xxxx,)

三、dict(hello=fun()) 或 {“hello”:fun()}
第拾二个参数,是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学习交换壹,二群已满.

Linux and
python学习沟通3群新开,欢迎参预,1起学习.qq 3群:5632278玖肆

不前进,不倒退,甘休的图景是尚未的.

同步前行,与君共勉,

 

四      使用元类

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

1      引子

相关文章

发表评论

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

网站地图xml地图