使用元类 编写ORM
元类
一句话: 元类定制类的创建行为
知识点
1.类的创建: python这种动态语言,函数和类的定义,不是编译时定义的,而是运行时动态创建的。
Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class。
2.控制类的创建行为,除了手动调用type()外,还可以使用metaclass。
3.生成类实例的执行顺序: 扫描类的定义准备生成对象->等会,先去生成父类->还不行,先去父类的元类那里看看有什么指示
-->执行元类的__new__()-->执行父类的__init__-->子类创建
元类基本功能 定制类
类似于父类继承,比如给子类添加一个方法属性:
'''
metaclass所以必须从type
类型派生:通常写成以Metaclass
结尾
class ListMetaclass(type):
def new(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.new(cls, name, bases, attrs)
Python解释器在创建MyList时,要通过ListMetaclass.__new__()
来创建
class MyList(list, metaclass=ListMetaclass):
pass
'''
调用一下:
'''
L = MyList()
L.add(1)
'''
从上面代码中 注意到关键词type,要知道,python调用type()创建类的,如果我们在元类的定义里什么都不做,
也就是直接调用了type.new(cls, name, bases, attrs),岂不说明类的创建本来就是以type为元类的
所谓的元类不就是在准备type(MyList)时,临时插入一段代码?
元类定制类的创建行为
父类继承方式可以定制子类,但是你不能根据还未出生的子类动态调整
同一的元类的定制是可以被继承下去的
比如写ORM框架,一个类对应一个数据库的表,我们首先会想到将数据库的操作封装到类里面,但是我现在不是使用者,
不确定数据库里表的定义,那么相应类里面的属性也就不确定,下面这个类(表)只是形式是这样,类(表)名,字段(类的属性)
都是可以变化的,如果要写框架,也就是写父类Model时,怎么才能获取子类User(还不一定叫这名)的属性等信息呢?
'''
编写底层模块的第一步,就是先把调用接口写出来。
class User(Model):
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
创建一个实例:
u = User(id=12345, name='xcl', email='test@orm.org', password='pwd')
保存到数据库:
u.save()
'''
怎么获取/操作类的属性呢 <元类的__new__() 方法
在元类中,我们可以获取当前准备创建的类的基本信息,
new(cls, name, bases, attrs)参数分别是指: 当前准备创建的类的对象,准备创建的类名,父类集合,类的属性集合
注意__new__还在父类Model创建前,__new__里的attrs都是指类属性而不是实例属性. 相应的,User类中添加的都是类成员而非实例成员
流程如下:
创建一个User对象u-->先创建父类Model;
先创建父类Model-->调用父类Model的init;
调用父类Model的init-->Model的元类的new;
获取并操作子类User的属性和类名-->Model的元类的new;
调用父类的统一接口save-->获取并操作子类User的属性和类名;
属性和类名各不相同的子类-->调用父类的统一接口save;
虽然metaclass的编写会比较复杂,但ORM的使用者用起来简单。
-- 真正的代码在此 --
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__, self.name) #eg: <IntegerField:id>
class IntegerField(Field):
def __init__(self, name):
super().__init__(name, 'int')
class StringField(Field):
def __init__(self, name):
super().__init__(name, 'varchar(50)')
#attrs 是 类的属性集合
#__mappings__ __table__是自己添加的两个属性,他两在使用该元类创建的父类 Model中也能获取到
class ModelMetaClass(type):
def __new__(cls, name, bases, attrs):
if name == "Model": #不是实际使用的类,跳过
return type.__new__(cls, name, bases, attrs)
mapping = dict()
for k, v in attrs.items():
if isinstance(v, Field):
mapping[k] = v
for k in mapping.keys():
attrs.pop(k) #从类属性中删除该Field属性 否则,容易造成运行时错误(实例的属性会遮盖类的同名属性)
#实例 User(id=1234,..) 但是User类中也有id等属性
attrs['__mappings__'] = mapping ## 保存属性和列的映射关系
attrs['__table__'] = name #添加属性__table__存放准备创建的类的名字, 也就是表的名字
# 这样一来,父类Model才能获取它未出生的儿子的类名
return type.__new__(cls, name, bases, attrs)
class Model(dict,metaclass=ModelMetaClass): #指示使用ListMetaclass来定制类,传入关键字参数metaclass
def __init__(self, **kw):
return super().__init__(**kw)
def __getattr__(self, key): #m.key==>m[key]
try:
return self[key]
except KeyError as e:
raise AttributeError(r"'Model' object has no attribute %s" % key)
def __setattr__(self, name, value):#m.key=value ==> m[key]=value
self[name] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name) #v是一个Field对象
params.append('?') #sql的占位符 ?
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % ( ###模拟一下数据库操作
self.__table__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
###到使用部分就简单了
class User(Model):
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 下面实例化时的 id name等会和 上面的类的成员变量同名冲突, 幸好在元类里面attrs.pop()掉了
u = User(id=1234, name='xcl', email='chunlaixiao@163.com', password='123456')
u2 = User(id=788, name='wang', email='wang@163.com', password='123456')
u.save()
u2.save()
print(u.name) ###这是调用__getattr__()方法 返回u[name]
使用元类 编写ORM的更多相关文章
- Python 元类实现ORM
ORM概念 ORM(Object Ralational Mapping,对象关系映射)用来把对象模型表示的对象映射到基于 SQL 的关系模型数据库结构中去.这样,我们在具体的操作实体对象的时候,就不 ...
- 元类实现ORM
1. ORM是什么 ORM 是 python编程语言后端web框架 Django的核心思想,"Object Relational Mapping",即对象-关系映射,简称ORM. ...
- 04 -- 元类和ORM
本篇主要介绍元类,为什么说一切皆对象:如何动态的创建类等:以及ORM,即什么是ORM等知识 一.元类 1.1 在Python中一切皆对象 在学习元类中我们首先需要了解一个概念-- python中一切皆 ...
- 元类、orm
目录 一.内置函数exec 二.元类 1. 什么是元类 2. 元类的作用 3. 创建类的两种方法 4. 怎么自定义创建元类 三.ORM 1. ORM中可能会遇到的问题 2. ORM中元类需要解决的问题 ...
- 元类应用ORM实现
首先看下一个简单的例子 # 需求 import numbers class Field: pass class IntField(Field): # 数据描述符 def __init__(self, ...
- python-元类和使用元类实现简单的ORM
元类 面向对象中,对象是类的实例,即对象是通过类创建出来的,在python中,一切皆对象,同样,类也是一个对象,叫做类对象,只是这个类对象拥有创建其子对象(实例对象)的能力.既然类是对象,那么类是通过 ...
- Python面向对象篇之元类,附Django Model核心原理
关于元类,我写过一篇,如果你只是了解元类,看下面这一篇就足够了. Python面向对象之类的方法和属性 本篇是深度解剖,如果你觉得元类用不到,呵呵,那是因为你不了解Django. 在Python中有一 ...
- 神级程序员通过两句话带你完全掌握Python最难知识点——元类!
千万不要被所谓"元类是99%的python程序员不会用到的特性"这类的说辞吓住.因为 每个中国人,都是天生的元类使用者 学懂元类,你只需要知道两句话: 道生一,一生二,二生三,三生 ...
- Python中的元类
从前面"Python对象"文章中了解到,在Python中一切都是对象,类可以创建实例对象,但是类本身也是对象. class C(object): pass c = C() prin ...
随机推荐
- HTTP1.0、HTTP1.1 和 HTTP2.0 的区别
一.HTTP的历史 早在 HTTP 建立之初,主要就是为了将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器.也是说对于前端来说,我们所写的HTML页面将要放在我们的 web 服务器上 ...
- C#使用NPOI导出Excel
当记录数超出65536时,有两种方式处理: 一是调用WriteToDownLoad65536方法建立多个Excel. 二是调用WriteToDownLoad方法在同一个Excel中建多个Sheet. ...
- opencv学习之路(21)、模板匹配及应用
一.模板匹配概念 二.单模板匹配 #include "opencv2/opencv.hpp" #include <iostream> using namespace s ...
- day02编程语言,Python语言介绍,Python解释器安装,环境变量,Python代码执行,pip,应用程序使用文件的三步骤,变量,变量的三大组成,比较,pycharm
复习 重点: 1.进制转换:二进制 与十六进制 2.内存分布:栈区 与堆区 # 二进制1111转换十六进制 => 8 4 2 1 => f 10101100111011 => 2a7 ...
- 异常:unity3d ArgumentException: The Assembly System.Configuration is referenced by System.Data.
异常:ArgumentException: The Assembly System.Configuration is referenced by System.Data. But the dll is ...
- (转载)Unity_3DText穿透场景物体解决
在unity的3D物体下有一个3DText 物体.这个物体可以在空间坐标中显示文本文字. 如下图: 这个3D Text在场景中的显示会出现穿透3D物体的现象.如图:本来这个hello world 的文 ...
- 【JS】Js对json的转换
将json字符串转换为json对象的方法.在数据传输过程中,json是以文本,即字符串的形式传递的,而JS操作的是JSON对象,所以,JSON对象和JSON字符串之间的相互转换是关键 例如: JSON ...
- 解决依赖冲突:maven-enforcer-plugin插件
我们会经常碰到这样的问题,在pom中引入了一个jar,里面默认依赖了其他的jar包.jar包一多的时候,我们很难确认哪些jar是我们需要的,哪些jar是冲突的.此时会出现很多莫名其妙的问题,什么类找不 ...
- Struts2 环境配置
1.下载struts2开发包 2.将apps中的 Struts-blank.war 解压,里面的 lib 中就是所需jar包 3.新建一个web project项目,将jar包导入复制粘贴到lib中 ...
- Type I and type II errors | 第一类错误和第二类错误
偶尔能看懂,但是死活记不住,归根结底是没有彻底理解! Type I and type II errors - wiki type I error is the rejection of a true ...