概要
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。
1.简单的例子(add)
class ListMetaclass(type):
def __new__(cls,name,bases,attrs):
attrs['add'] = lambda self,value : self.append(value)
return type.__new__(cls, name, bases, attrs)
#有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:
class MyList(list, metaclass=ListMetaclass):
pass
new()方法接收到的参数依次是:
1.当前准备创建的类的对象;
2.类的名字;
3.类继承的父类集合;
4.类的方法集合。
测试一下MyList是否可以调用add()方法:
>>> L = MyList()
>>> L.add(1)
>> L
[1]
2.ORM例子(“Object Relational Mapping”,即对象-关系映射)
编写底层模块的第一步,就是先把调用接口写出来。比如,使用者如果使用这个ORM框架,想定义一个User类来操作对应的数据库表User,我们期待他写出这样的代码:
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
(1)创建一个实例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
(2)保存到数据库:
u.save()
(3)代码示例
class Field(object): #定义一个类
def __init__(self,name,column_type):
self.name = name #数据库表的「列名」(如 id、username)
self.column_type = column_type #数据库列的「数据类型」(如 varchar(100)、bigint)
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__,self.name) #返回类名和列名
class StringField(Field): #继承 Field 类
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field): #继承 Field 类
def __init__(self,name):
super(IntegerField,self).__init__(name,'bigint')
class ModelMetaclass(type):
# cls:元类本身(ModelMetaclass)
# name:要创建的类的名字(如 User、Blog)
# bases:要创建的类的父类列表(如 [Model])
# attrs:要创建的类的属性字典(如 {'id': IntegerField('id'), 'name': StringField('name')})
def __new__(cls, name, bases, attrs):
# 1. 跳过基类「Model」的处理(只处理 Model 的子类)
if name == 'Model':
return type.__new__(cls, name, bases, attrs)
# 2. 打印日志,提示找到一个数据库表对应的类(方便调试)
print('Found model: %s' % name)
# 3. 收集类中所有「Field 类型的属性」,构建字段映射字典
mappings = dict()
for k, v in attrs.items():
# 判断属性值是否是 Field 的实例(包括 StringField、IntegerField 子类)
if isinstance(v, Field):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v # 保存「类属性名 → 字段实例」的映射(如 'id' → IntegerField('id'))
# 4. 从类属性中删除已收集的 Field 实例(避免属性冲突,后续通过 __mappings__ 访问)
for k in mappings.keys():
attrs.pop(k)
# 5. 为类添加核心属性:
attrs['__mappings__'] = mappings # 保存字段映射关系(关键!后续 SQL 操作依赖它)
attrs['__table__'] = name # 数据库表名 = 类名(简化设计,可自定义修改)
# 6. 调用 type 的 __new__ 方法,创建并返回这个新类
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 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))
小结
metaclass是Python中非常具有魔术性的对象,它可以改变类创建时的行为。这种强大的功能使用起来务必小心。

被折叠的 条评论
为什么被折叠?



