先来了解下python魔法的内核吧:
一切皆对象
- 一切皆对象
- 一切皆有类型
- “class” and "type" 之间本质上上并无不同
- 类也是对象
- 他们的类型都是type
class ObjectCreator(object):
pass
这段代码将在内存中创建一个对象,名字叫ObjectCreator.
这个对象(类)自身拥有创建对象的(类实例的能力),##
而这也就是它为什么是一个类的原因##
你可以对该对象进行以下操作:
- 将它赋值给一个变量
- 可以拷贝它
- 可以为它增加属性
- 可以将其作为函数参数进行传递
#交互型
>>> def echo(o):
... print(o)
...
>>> echo(my_object)
<__main__.ObjectCreator object at 0x7fcf7d0d6c50> #你可以将类做为参数传给函数
>>> ObjectCreator.new_attrituer=echo # 你可以为类增加属性
>>> print(hasattr(ObjectCreator,'new_attrituer'))
True
>>> print(ObjectCreator.new_attrituer) #新加的类的方法属性
一:用type创建一个类
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
#三个参数的类型分别是str,tuple,dic
比如:
#目标类:
class Animal(object):
def __init__(self,name):
self.name=name
def eat(self):
pass
def go_to_eat(self):
pass
#用type创建
def init(self,name):
self.name=name
def eat(self):
pass
def go_to_eat(self):
pass
Animal=type('Animal',(object,),{
'__init__':init,
'eat':eat,
'go_to_eat':go_to_eat
})
>>>print(Animal)
这样我们实现了用type去动态创建一个类的
BUT~这样是不是太麻烦了!!!(抬走下一位!)
二:metaclass登场
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
# metaclass是创建类,所以必须从`type`类型派生:
class Listmetaclass(type):
def __new__(cls,name,bases,attrs):
attrs['add']=lambda self,value:self.append(value)
return type.__new__(cls,name,bases,attrs)
class Mylist(list):
__metaclass__=Listmetaclass
- 按照默认习惯.metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass
- 当我们写下__metaclass__=Listmetaclass时候,它表示解释器在创建Mylist的时候
,要通过Listmetaclass的__new__()方法创建
在此,我们可以修改类的定义,比如:加上新的方法,然后返回修改后的定义. - __new__()方法接受的参数依次是:
- 当前准备创建的类的对象
- 类的名字
- 类继承的父类的集合
- 类的方法的集合
元类的主要目的就是为了当创建类时能够自动改变类.
使用到元类的代码比较复杂,这背后的原因倒并不是因为元类本身,
而是因为你通常会使用元类去做一些晦涩的事情,
依赖于自省,控制继承等等。
确实,用元类来搞些“黑暗魔法”是特别有用的,
因而会搞出些复杂的东西来。
就这个例子来说:
- 拦截类的创建(拦截Mylist的创建)
- 修改类(给Mylist增加add的方法)
- 返回修改之后的类
**“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” **-----Tim Peters
元类的主要作用的创建API,一个典型的例子是 ORM.
ORM全称“Object Relational Mapping”,
即对象-关系映射,就是把关系数据库的一行映射为一个对象,
也就是一个类对应一个表,
这样,写代码更简单,不用直接操作SQL语句。
~
要编写一个ORM框架,所有的类都只能动态定义,
因为只有使用者才能根据表的结构定义出对应的类来。
我们来尝试写一个简单的ORM框架吧
首先我们要知道使用者会调用什么接口:
比如,使用者可能会定义一个User类来操作数据表User.
class User(Model):
#创建User表
#创建四列属性
id=IntegerField('id')
name=StringField('username')
email=StringField('email')
password=StringField('password')
# 创建一个实例:
u=User(id='12345',name='zhou',email='[email protected]',password='123455')
#保存到数据库
u.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>' %(self.__class__.__name__,self.name)
在Field的基础上,我们来定义各种Field:
#为了简化,我们先定义好各种属性的type
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
def __init__(self,name):
super(IntegerField,self).__init__(name,'bigint')
接下来,我们要编写元类了
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
#如果是基类,直接返回(即不对model类进行修改)
if name=='Model':
return type.__new__(cls,name,bases,attrs)
print('Found model: %s' %name)
#如果是其他类,则进行装饰
#取出所有类属性,将其放入mapping
mappings=dict()
for key,value in attrs.iteritems():
if isinstance(value,Field):
print('Found mapping: %s==>%s' %(key,value))
mappings[key]=value
for key in mappings.iterkeys():
attrs.pop(key)
attrs['__mappings__']=mappings
attrs['__table__']=name
return type.__new__(cls,name,bases,attrs)
编写基类:
class Model(dict):
__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 key,value in self.__mappings__.iteritems():
fields.append(value.name)
params.append('?')
args.append(getattr(self,key,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)时候,
解释器首先在当前类User的定义中寻找
metaclass,如果没找到,就在继承的父类中继续找.,
找到了,就用model中的定义的ModelMetaclass去创建User类
也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。
在ModelMetaclass中做的事情:
- 排除对Model 的修改
- 在当前类 中查找定义的类的属性,如果找到一个FIeld属性,就保存到__mappings__字典中
同时从类属性中删除Field属性,否则容易造成运行时的错误 - 把表名保存到__table__中
在Model 类中,就可以定义各种操作数据库的方法了(列属性保存在__mappings__)中
全部代码:
#!/usr/bin/python3
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)
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
def __init__(self,name):
super(IntegerField,self).__init__(name,'bigint')
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
if name=='Model':
return type.__new__(cls,name,bases,attrs)
print('Found model: %s' %name)
mappings=dict()
for key,value in attrs.iteritems():
if isinstance(value,Field):
print('Found mapping: %s==>%s' %(key,value))
mappings[key]=value
for key in mappings.iterkeys():
attrs.pop(key)
attrs['__mappings__']=mappings
attrs['__table__']=name
return type.__new__(cls,name,bases,attrs)
class Model(dict):
__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 key,value in self.__mappings__.iteritems():
fields.append(value.name)
params.append('?')
args.append(getattr(self,key,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')
u=User(id='12345',name='zhou',email='[email protected]',password='123455')
u.save()
运行结果:
Found model: User
Found mapping: email==>
Found mapping: password==>
Found mapping: id==>
Found mapping: name==>
SQL:insert into User(password,email,username,id) values(?,?,?,?)
ARGS:['123455', '[email protected]', 'zhou', '12345']
最后说几句:
-
元类(Metaclass)类似于装饰器,可以在类的创建时动态修改类.
-
元类语法糖:__metaclass__
学习参考:
廖雪峰Python教程
深入理解元类
5分钟理解元类