澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

模型中一些自定义的总结,Django的models实现分析

2019-04-26 作者:www.8455.com   |   浏览(159)

python元类:**type()   **

 

 

Python 中 Meta Classes详解,pythonclasses

接触过 Django 的同窗都应该丰裕熟悉它的 O君越M 系统。对于 python 新手来讲,那是壹项大概能够被称作“黑科技(science and technology)”的风味:只要您在models.py中不管定义三个Model的子类,Django 便得以:

  1. 获得它的字段定义,并转移成表结构
  2. 读取Meta内部类,并转化成相应的计划音讯。对于特种的Model(如abstract、proxy),还要实行相应的改动
  3. 为未有定义objects的Model加上二个私下认可的Manager

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

运转时,遍历models.py中的全体属性,找到Model的子类,并对其举行上述的改变。
当年,作者还认为本人触碰到了真理,并曾将其采用到骨子里生育中——为 SAE 的 KVDB 写了七个类 O凯雷德M 系统。然则在落到实处的经过中,笔者明显感受到了那种艺术的丑陋,而且质量并不特出(因为要遍历全部的概念模块)。

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

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

那句话揭露了生命的美妙所在:真正的性命都以由基本物质自发组成的,而非造物主流水生产线式的加工。

那么,假如 类 也有性命来讲,对它和睦的梳洗就不应有由调用者来落成,而应该是天赋的。

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

元类 是什么?

简轻便单说:元类就是类的类。

率先,要有1个概念:

python 中,1切都以对象。

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

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

类的类: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 简介

模型中一些自定义的总结,Django的models实现分析。从 官方文书档案 中,大家得以明白:

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

有没认为上面第三段示例某个鬼畜呢?它勒令程序猿将类的积极分子写成二个字典,几乎是反人类。若是我们的确是要因而改变元类 来改动 类 的作为的话,就好像就不可能不运用那种艺术了~~简直可怕~~

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

class Integer(int):

  __metaclass__ = IntMeta

现今将会等价于:

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

由此一来,我们在动用守旧类定义的还要,也得以接纳元类啦。

栗子:子类净化器

急需描述

你是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".

看,即便像最终三个事例中的私有属性也难逃审查,因为它们本质都以平等的。

竟然,你还是能对有标题的性质举办私行的改变,比如让不文明的函数在调用时打出1行警告 等等,那里就不多说了。

元类 在其实付出中的应用

一般性支出时,元类 常用吧?

当然,Django 的 O中华VM 就是二个例子,威名昭著的 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__ 方法,修改 类定义 的行为

一.自定义管理器(Manager)

在语句Book.objects.all()中,objects是贰个分外的天性,通过它来查询数据库,它就是模型的二个Manager.
各类Django模型至少有三个manager,你能够创建自定义manager以定制数据库的访问.
此处有多个点子创造自定义manager:加多额外的manager;修改manager再次回到的初始Queryset.

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

1      引子

1      引子

您大概感兴趣的稿子:

  • Python中的Classes和Metaclasses详解

中 Meta Classes详解,pythonclasses 接触过 Django 的同窗都应有特别熟稔它的 O猎豹CS6M 系统。对于 python 菜鸟来讲,那是壹项差不离能够被称作“黑科...

增添额外的manager

扩张额外的manager是为模块加多表级功效的首荐办法.(至于行级作用,也便是只效劳于模型实例对象的函数,则经过自定义模型方法落成).
比方,为Book模型加上二个title_count()的manger方法,它接受1个keyword,并赶回标题中隐含keyword的书的数量.

#medols.py
from django.db import models

class BookManager(models.Manager):
    def title_count(self, keyword):
        return self.filter(title_icountains=keyword).count()


class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    ...
    objects = BookManager()

    def __str__(self):
        return self.title

1.我们创造3个BookManager类,承继自django.db.models.Manager.它唯有1个主意title_count(),来拓展计算.注意,那几个格局运用了self.filter(),这个self指manager本身.
二.将BookManager()赋值给模型的objects属性.它将顶替模型的私下认可manager(objects).把它定名称叫objects是为着与暗中同意的manager保持一致.
当今大家能够举行上面包车型客车操作:

>>> Books.objects.title_count('django')    #这是我们自定义的manager中的查询方法
2
>>> Books.objects.filter(title__icontains='django').count()    # 默认的查询方法依然可用
2

那般我们得以将日常使用的查询进行李包裹装,就不要再次写代码了.

 

1.1     神奇的Django中的models

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

设置数据库models代码:

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

此地有多少个美妙的地点,涉及到了python中最隐秘的几特性状。

先看下有怎么样美妙的地点:

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

那边用的是python的两个语法个性:

  • 讲述符协议
  • 元类

我们来一步一步解开神秘面纱。

1.1     神奇的Django中的models

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

安装数据库models代码:

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

那边有多少个美妙的地方,涉及到了python中最隐私的多少个特征。

先看下有何样美妙的地点:

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

此处用的是python的五个语法特性:

  • 讲述符协议
  • 元类

我们来一步一步解开神秘面纱。

修改起首Manager Queryset

manager的根基Queryset重临系统中的全数对象.譬喻,Book.objects.all()归来book数据库中的全数书籍.你而已透过覆盖Manager.get_queryset()办法来重写manager的根基Queryset.get_queryset()有道是根据你的需求重临三个Queryset.
举例,下边包车型地铁模子有八个manger--四个回去全体目的,另多少个仅重返作者是Roald Dahl的书

from django.db import models

#首先,定义一个Manager的子类
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')


# 然后,将它显式地插入到Book模型中
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)
    ...
    objects = models.Manager()    # 默认Manager
    dahl_objects = DahlBookManager()    # 自定义的特殊Manager

在这几个示例模型中,Book.objects.all()将回到数据库中的全体图书,而Book.dahl_objects.all()只回去笔者是Roald Dahl的书籍.注意我们明确的将objects设置为暗中认可Manger的二个实例,因为只要我们不这么做,那么dahl_objects将造成唯一多个可用的manager.
由于get_queryset()归来3个Queryset对象,所以你能够利用filter(),exclude()和别的具备的Queryset方法.

即便您利用自定义的Manager对象,请留心,Django蒙受的首先个Manager(以它在模型中被定义的岗位为准)会有贰个例外情状。 Django将会把首个Manager 定义为私下认可Manager ,Django的不在少数有的(不过不包涵admin应用)将会分明地为模型使用那一个manager。 结论是,你应该小心地挑选你的默许manager。因为覆盖get_queryset()了,你大概接受到二个空头的回来对像,你不能够不制止那种景况.

元类实际上做了以下叁上边包车型大巴干活:

二      数据校验

二      数据校验

2.自定义模型方法

为了给您的对像增加一个行级功用,那就定义一个自定义方法.鉴于manager日常被用来用一些整表操作(table-wide).模型方法应该只对新鲜模型实例起作用.

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        # Returns the person's baby_boomer status
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return 'Pre-boomer'
        elif self.birth_date < datetime.date(1965, 1, 1):
            return 'Baby boomer'
        else:
            return 'Post-boomer'

    def _get_full_name(self):
        # Return the person's full name
        return f'{self.first_name} {self.last_name}'
    full_name = property(_get_full_name)    # 将类方法包装为属性

那个点子的应用:

>>> p = Person.objects.get(first_name='Barack', last_name='Obama')
>>> p.birth_date
datetime.date(1961, 8, 4)
>>> p.baby_boomer_status()
'Baby boomer'
>>> p.full_name  # 注意这不是一个方法 -- 它被视为一个属性
'Barack Obama'

 

二.一     数据校验难题

Python尽管是强类型的脚本语言,不过在概念变量时却力不从心钦赐变量的档期的顺序。

诸如,我们在Student类中定义1个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")

 

然则,假使更加大年龄时就会蒙受标题,不可能重用校验逻辑。

有未有简要的不二诀要吧?

二.一     数据校验难题

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

 

可是,假使更新年龄时就会跨越标题,不恐怕重用校验逻辑。

有未有简短的章程吧?

三.重写预约义的模型方法

再有壹组模型方法了包装了一部分您只怕想要自定义的数据库行为.特别是你可能想要修改save()delete()的专门的学问格局.你能够专断的重写这几个方式(以及任何的模子方法)来退换行为.重写内置方法的经文用例便是您想要在保留一个目标是做些其余的哪些.比方:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs)    #Call the "real" save() method.
        do_something_else()

您也得以阻止保存行为:

from django.db import models


class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        if self.name == 'Yoko Ono's Blog':
            return    # Yoko shall never have her own blog!
        else:
            super(Blog, self).save(*args, **kwargs)    #Call the "real" save() method

纪事,承袭超类的法子十分重大,即super(Blog, self).save(*args, **kwargs),它确定保障该目标仍被封存到数据库中.假如你忘掉调用超类方法,那么暗中同意的行事将不会发生,也不会发出数据库操作.
同一关键的是,您要传送能够传递给模型方法的参数——那正是*args, **kwargs所做的政工。Django将不时扩大内置模型方法的功能,并加多新的参数。假设您在措施定义中利用了*args, **kwargs,您将确认保证你的代码在增多时将机关协助那一个参数。

  • 干预创制类的长河
  • 修改类
  • 归来修改现在的类

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

Model.clean()

运用这几个措施来提供自定义的模型验证,以及修改模型的属性.例如,你能够采纳它来给1个字段自动提供值,只怕用于多少个字段须要一齐验证的动静:

import detetime
from django.core.exceptions import ValidationError
from django.db import models

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date
        if self.status == 'draft' and self.pub_date is not done:
            raise ValidationEroor('Draft entries may not have a publication date')
        #Set the pub_date for published items if it hasn't been set already
        if self.status == 'published' and self.pub_date is None:
            self.pub_date = datetime.date.today()

瞩目,调用模型的save()方法时,不会自动调用clean()主意,须要views手动调用.
上边的示范中,clean()引发的ValidationError至极通过2个字符串实例化,所以它将被保存在三个奇特的谬误字典中,键为NON_FIELD_ERRORS.这些键用于全数模型出现的不当而不是2个特定字段穿线的荒唐:

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

若要引发一个一定字段的丰硕,能够接纳二个字典实例化ValidationError,其中字典的键为字段名.大家能够立异前边的例子,只抓住pub_date字段上的尤其:

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError({'pub_date': 'Draft entries may not have a publication date.'})
        ...

参考: https://djangobook.com/advanced-models/
   http://python.usyiyi.cn/translate/django_182/ref/models/instances.html

 

3      Python描述符

讲述符提供了优雅、简洁、健壮和可采用的解决方案。简单来讲,三个讲述符就是多少个目的,该目的表示了多天性质的值。

那就表示固然二个Student对象有二个个性“name”,那么描述符就是另2个能够用来表示属性“name”持有值的对象。

讲述符协议中“定义了__get__”、“__set__”或”__delete__” 那一个卓殊措施,描述符是达成当中二个或多少个方法的对象。

3      Python描述符

叙述符提供了优雅、简洁、健壮和可选择的解决方案。简单的说,3个讲述符就是二个目标,该目标表示了三日性质的值。

那就象征倘诺二个Student对象有3个属性“name”,那么描述符便是另贰个可知用来表示属性“name”持有值的对象。

叙述符协议中“定义了__get__”、“__set__”或”__delete__” 那一个特殊措施,描述符是完结当中一个或四个方式的对象。

为啥使用元类?

 

缘何要使用元类那种歪曲且易于出错的功能?
一般情状下,大家并不会采纳元类,9九%的开辟者并不会用到元类,所以一般不要考虑这几个主题素材。
元类主用用于成立API,二个首屈一指的例子就是Django的OOdysseyM。
它让大家得以如此定义三个类:

 

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()})

1、class 自定义的类名称

二、(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群新开,应接参与,一齐学习.qq 三群:5632278玖四

不前进,不倒退,甘休的场馆是未曾的.

一齐前进,与君共勉,

 

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时不太1致,在选择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

 

这几个版本存在2个难点,就是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

 

本条本子还设有1个题材,要是3个类别有多个字段使用了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的中二个困难,在大部光景下都不会用到。但是在编辑框架方面却是必不可少乏的利器。

四      使用元类

元类是python的中3个困难,在半数以上风貌下都不会用到。可是在编写制定框架方面却是必不可贫乏的利器。

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

 

语法解释:

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

假设对于python面向对象编制程序有掌握的话,python的具有类都承接自type,type是全部类的元类。。

在此处,大家自定义的元类EntityMeta,具备1个效用正是判断类属性是不是为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

 

语法解释:

本子3相对来讲版本二,最大的转移在于Student类承接了自定义元类EntityMeta。

只要对于python面向对象编制程序有询问的话,python的保有类都承接自type,type是全部类的元类。。

在此间,我们自定义的元类EntityMeta,具有二个功能正是推断类属性是还是不是为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      原掌握释

伍.壹     属性读取顺序

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

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

先记住那个顺序,前边精晓描述须要。属性描述符都是概念在类中的,而不是在对象中。

5.一     属性读取顺序

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

  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__ 中的3个或两个(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类,暗许会创立一些办法。

元类决定了类出初步化后有何样特色和行为。若是咱们想自定义八个类,具有某种特殊的作为,则需求自定义元类。

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

 

六  别的案例

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

6  其余案例

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

七      参考资料

编号

标题

链接

1

元类

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

2

描述符

http://python.jobbole.com/81899/

3

《流畅的python》

元类部分

七      参考资料

编号

标题

链接

1

元类

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

2

描述符

http://python.jobbole.com/81899/

3

《流畅的python》

元类部分

本文由澳门新萄京官方网站发布于www.8455.com,转载请注明出处:模型中一些自定义的总结,Django的models实现分析

关键词: