在Python中,类也是一个对象,比如说,我们可以实现一个返回类的函数:
1
2
3
4
|
def createClass(inner_attr):
class NewClass(object):
attr = inner_attr
return NewClass
|
这与静态语言有很大的不同,可以看见NewClass这个类是在运行时创建的,而静态语言实现起来十分麻烦。能够动态创建类的能力在某些时候可以优雅地实现一些功能(见下文)。而要实现动态地创建自定义类,则需要了解Python类创建的原理。
Type()函数
我们知道在写Python的时候class
关键字用于定义一个类。然而实际上我们也可以用type
函数来创建一个类:
1
2
3
4
5
6
7
|
NewClass = type('MyNewClass', (object, ), {'my_class_attr':"123"})
n = NewClass()
'''
type第一个参数:类的名字
type第二个参数:类所继承的父类
type第三个参数:类的attribute
'''
|
如何动态生成类
除了type()
之外,我们还可以用metaclass
来定义类的创建:
1
2
3
4
5
6
7
8
9
10
|
class MeowMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['meow'] = lambda self: print("meow")
return type.__new__(cls, name, bases, attrs)
class Dummy(metaclass=MeowMetaclass):
pass
d = Dummy()
d.mewo() # meow
|
在上面的例子中,我们定义了一个Meow元类,让Dummy类的实例化时加入一个Meow函数。可以发现,python在创建Dummy类的时候,会调用其Metaclass的__new__来进行类的实例化,于是我们便可以在实例化时做事情,最后再把动态修改的类进行返回。
声明metaclass时,有几点要注意:
- metaclass需继承type
- 通过__new__(cls, name, bases, attrs)来实现类的动态生成
- 需要动态的类要指定metaclass:
class A(metaclass=XXX):...
廖雪峰的metaclass的文章中讲到了metaclass的一个应用场景:来实现ORM,实际上,这也是django的做法,有兴趣的读者可以去读一下django model的源码。
首先展示一下这个简单ORM的效果:
1
2
3
4
5
6
7
8
9
|
class User(Model):
id = IntergerField('id')
name = StringField('string')
# 创建user实例
u = User(id = 123, name = "raynor")
print(u.id) # 123
# 保存到数据库
u.save()
|
对于使用者来说,要使用ORM来进行对象管理,只需要创建一个继承Model
的类,同时用XXXField
来定义具体的字段即可。
当然我们需要定义Field,它对应数据库的Column:
1
2
3
4
5
6
7
8
|
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
class StringField(Field):
def __init__(self, col_name):
super().__init__(col_name, 'varchar(100)')
|
我们希望用户的ORM类,比如说User,在创建时能读取根据声明的Field,这时便可用到metaclass,在model类的创建时做把field转化成attribute:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class ModelMetaClass(type):
def __new__(cls, name, bases, attrs):
if name == 'Model':
return type.__new__(cls, name, bases, attrs)
mappings = dict()
# 把所有field推出attribute并加入__mappings__统一维护
for k, v in attrs.items():
if isinstance(v, Field):
mappings[k] = v
for k in mappings.keys()
attrs.pop(k)
attrs['__mappings__'] = mappings
attrs['__table__'] = name
return type.__new__(cls, name, bases, attrs)
|
实际的Model类:
1
2
3
4
5
6
7
8
9
10
11
12
|
class Model(metaclass=ModelMetaClass):
def __init__(self, **kw):
super().__init__(**kw)
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))
|
总结一下,我们通过定义元类,让具体的orm类继承Model
类,那么在__new__
的时候,python便会查找父类的__new__
函数,也就是Model的__new__
函数,继而自动调用ModelMetaClass
的__new__
函数,其中我们把所有的field字段转化成具体的model的attribute,并缓存到具体类的实例上。最后在save的时候调用这个缓存,读取其实例上的对应的值,最后生成出sql语句。
到这里save()只需要实际连接数据库并执行即可。如此,我们便实现了简答的ORM框架,让程序员可以舒服地编写具体的对象。
Reference
使用元类
What are metaclasses in Python?