先说type()
动态语言和静态语言最大的不同,是 函数和类的定义不是编译时定义的,而是运行时动态创建的
下面我们创建一个类
如果我们按照普通的写法写的话
我们看到
Hello类的类型是 type
而h的类型是Hello类
上面我们说到类(class)的定义是在运行时动态创建的,那么这个Hello类其实就是动态创建的,而type()方法,其实就是动态创建时调用的方法
所以现在,我们知道了type()的作用了吧:
- 返回一个对象的类型
- 还可以创建出新的类,比如我们可以通过type()创建出上面定义的Hello类
我们来定义一下:
先对使用type()去创建类的实例方法做一个说明
Hello=type('Hello',(object,),dict(hello=func))
- 参数1:表示class的名字
- 参数2:表示继承父类的集合,tuple类型(所以只有一个父类时,后边添加了“,”逗号)
- 参数3:class 方法名称与现有函数绑定,这里表示指定hello函数,并且绑定了func
这么一说是不是就明白了呢。哈哈
metaclass-元类
除了上面的使用type()动态创建类之外,还可以使用metaclass
其实metaclass的作用就是:
定义类
,创建类,有了类之后再创建对象 这整个过程中的第一步
(所以特地用红色字来表示)
我们来先看例子,需要注意的是,当我们在定义metaclass时,最好是以Metaclass为后缀,以便于区分
-
定义metaclass类
class MyListMetaclass(type): def __new__(cls,name,bases,attrs): attrs['add']=lambda self,value:self.append(value) return type.__new__(cls,name,bases,attrs)
-
有了metaclass之后,我们去创建一个类
#在创建类时,需要传入关键字参数metaclass class MyListClass(metaclass=MyListMetaclass): pass
这样,python解释器在创建MyClass时,就会通过MyMetaclass.__new__()
来进行创建
所以我们可以在MyMetaclass.__new__()
方法中添加新的方法,然后返回修改之后的定义
new方法接收到的参数依次是:
- cls--当前准备创建的类的对象
- name--类的名字
- bases--类继承的父类集合
- attrs--类的方法的集合
我们上方的示例演示的是创建一个 list列表一样的类,这个类拥有add方法,其实就是封装了append方法
我们来看下完整示例
这个时候你会想,动态修改有什么意义?直接在MyList定义中写上add()方法不是更简单吗?正常情况下,确实应该直接写,通过metaclass修改纯属变态。
对,对于简单的逻辑,如果你还使用metaclass那就是
变态
但是如果对于复杂的逻辑,比如ORM,那么用metaclass能达到
事半功倍
的效果
使用metaclass 完成ORM框架
ORM简介
ORM全称:“Object Relational Mapping”即对象-关系-映射
就是把关系数据库的一行映射为一个对象
也就是一个对象对应于一个表
这样一来,写代码更加简单,不用直接操作sql语句
要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来
下面,我们尝试编写一个ORM框架
####第一步、编写底层框架的第一步:
#### 是将调用逻辑写出来,比如我们要定义ORM,那么先定义调用逻辑
#### 这里我们定义User类来操作对应的数据库表User,我们想这样使用
class User(Model):
#定义类的属性到数据库列中的映射
id= IntegerField('id')
name=StringField('username')
email=StringField('email')
password=StringField('password')
#创建一个实例
u=User(id=12,name='xiaoming',email='guaju@126.com',password='123456')
#创建实例,意味着做了数据库的新增记录的操作,下一步直接保存即可
u.save()
好,我们现在讲操作逻辑写出来了,现在我们分析一下具体的操作逻辑中使用到的类及方法
- Model 父类
- IntegerField、StringField 整数 字符类型属性
这两类是由ORM框架来提供,剩下的save()方法则由metaclass来完成,因为save()操作就等同于动态处理了
好,我们接着来实现
第二步、定义Field类,他负责保存数据库表的字段名和字段类型
class Field(object):
def __init__(self,name,column_type):
self.name=name
self.column_type=column_type
def __str__(self):
return <%s:%s> %(__class__.__name__,self.name)
在Field基础上,定义一些我们需要的子类,这里,我们就定义IntegerField和StringField两个就够了
class IntegerField(Field):
def __init__(self,name):
super(IntegerField,self).__init__(name,'bigint')
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
第三步、编写最复杂的的ModelMetaclass类了
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
if name=='Model':
return type.(cls,name,bases,attrs)
print('Found Model:%s' %name)
mappings=dict()
for k,v in attrs.items():
if isintance(v,Field):
print('Found Mapping:%s-->%s' %(k,v))
mappings[k]=v
for k in mappings.keys():
attrs.pop(k)
#保存属性和列的对应关系
attrs['__mappings__']=mappings
#假设表名和类名一致
attrs['__name__']=name
return type.__new__(cls,name,bases,attrs)
基类Model
class Model(dice,metaclass=ModelMetaclass):
def __init__(self,**kw):
super(Model,self).__init(**kw)
def __getattr__(self,key):
try:
return self[key]
except KeyError:
raise AttributeError(r“’‘Model' object has no attribute '%s'” %key)
def __setattr__(self,key,value):
self[key]=value
def save(self):
fields=[]
params=[]
args=[]
for k,v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
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))
欧了,代码部分完事了
现在说一下逻辑
- 当用户创建一个User类时,Python解释器会首先在当前类User查找metaclass,如果没有找到,就继续在父类Model中查找metaclass
- 找到了,就使用该metaclass去创建User类
- 在Modelmetaclass中,会做这些事
- 排除掉对Model类的修改
- 查找当前类User定义的所有属性,找到一个就放到mappings这个dict中去,同时从类属性中删除该Field属性,否则容易造成运行时错误
实例的属性会遮盖类的同名属性
- 把表名保存到table中,这里简化,表名为类名
然后在Model中就可以定义各种操作数据库的方法,比如save(),delete(),find(),update()等
我们实现了save方法,即把一个实例保存到数据库中,因为有表名以及属性到字段的映射和属性值的集合,就可以构造出Insert语句
这样,这么点代码就实现了ORM框架的搭建,是不是很简单
。。。。。。
反正我写完这个文章后,我的内心是崩溃的。。。不过看不懂没关系,看懂了最好,因为元类这个东西,暂时对你们没什么帮助哦,当你们用到了这个东西,再回过头来看也不迟。
网友评论