Python中的metaclass
我们知道,当一个类定义了,那么类其中的成员变量和函数也就确定了。而metaclass就是一种在运行时动态设置成员变量和函数的一种方法
type()函数
type(name, bases, dict)
类名称(name)、父类列表(bases)和 属性字典(dict) “
python中一个类的定义可以通过 class 类名(父类)
来定义,同样可以通过上面的type函数形式定义
>>> class user(object):
... user = "user"
... key = "key"
...
>>> print(user)
<class '__main__.user'>
>>> u = user()
>>> u.user
'user'
>>> u.key
'key'
再看使用type定义
>>> user = type("user",(),{"name":"user","key":"123456"})
>>> u = user()
>>> u.name
'user'
>>> u.key
'123456'
其中可以看到user类中成员函数和变量都是用dict存放的,那么,我们只需要动态的改变dict中的内容就对应修改了这个类的成员变量和函数
metaclass 类
定义一个类,表示用此类或子类的定义的变量是可以改变的
class Field(object):
def __init__(self,):
pass
再定义几个子类,同样用这些类定义的变量都是可以改变的
class IntegerField(Field):
def __init__(self):
super(IntegerField, self).__init__()
class StringField(Field):
def __init__(self):
super(StringField, self).__init__()
定义三个成员变量,其中两个是可以改变的
class User(Model):
age = 20
name = StringField()
sex = IntegerField()
此处到了重点,什么是元类,元类就是创建类的类。也可以说他就是一个类的创建工厂,如 type 函数。
User 中有__metaclass__
这个属性吗?如果有,那么Python会在内存中通过__metaclass__
创建一个名字为User 的类对象。如果Python没有找到__metaclass__
,它会继续在自己的父类Model中寻找__metaclass__
属性,并且尝试以__metaclass__
指定的方法创建一个User 类对象。如果Python在任何一个父类中都找不到__metaclass__
,它也不会就此放弃,而是去模块中搜寻是否有__metaclass__
的指定。如果还是找不到,那就是使用默认的type来创建User
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
注意到模块集成自dict,有__getattr__
和__setattr__
方法,那么只要将任意变量名的成员赋值,就相当于为类添加了成员函数
class ModelMetaclass(type):
# __new__方法接受的参数依次是:
# 1.当前准备创建的类的对象(cls)
# 2.类的名字(name)
# 3.类继承的父类集合(bases)
# 4.类的方法集合(attrs)
def __new__(cls, name, bases, attrs):
# 排除Model类本身:
if name == 'Model':
return type.__new__(cls, name, bases, attrs)
# 获取所有的Field和主键名:
mappings = dict()
fields = []
for k, v in attrs.items():
if isinstance(v, Field):
mappings[k] = v
# 删除成员变量重新构造
for k in mappings.keys():
attrs.pop(k)
return type.__new__(cls, name, bases, attrs)
注意:
其中,attrs.pop(k)
将我们事先定义好的成员变量删除,那么起始状态,其中并没有name和sex字段
由于Model集成自dict,User也继承自dict。之前提到过,类的成员变量和函数都是以dict存放,这样ModelMetaclass类__new__
函数中传入的参数attr就是它自生,那么当我们往Model中添加数据时,相当于给User添加成员变量
u1 = User(name="user",sex=1) #添加了name和sex字段
u2 = User(name="user") #只添加了name字段
u3 = User(sex=1) #只添加了sex字段