Contents

如何在Python中动态地创建Class

在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实现简单的ORM

廖雪峰的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?