引言
python中的元类MetaClass:本质也是一个类,但和普通类的用法不同,它可以对类内部的定义(包括类属性和类方法)进行动态的修改。
换句话说,使用元类的主要目的是为了实现在创建类时,能够动态的改变类中定义的属性或者方法。
1、自定义元类创建类的基本方法
第一步:创建元类,一个类必须继承type才能使其成为一个元类;
第二步:创建类,使用关键词参数metaclass指定创建它的元类。
2、元类的__new__方法
在类的创建过程中,元类的__new__方法和__init__方法会被先后调用,前者负责创建类,后者负责类的初始化。在实际使用中,大多类的初始化工作都可以放在__new__方法中进行,所以为了代码简洁往往只需要定义__new__方法。
class FirstMetaClass(type):
'''
cls:代表动态修改的类
name:代表动态修改的类名
bases:代表被动态修改的类的所有父类
attr:代表被动态修改的类的所有属性、方法组成的字典
'''
def __new__(cls, name, bases, attrs):
attrs['name'] = 'C语言中文网'
attrs['say'] = lambda self: print('调用say()实例方法')
return super().__new__(cls, name, bases, attrs)
# 通过FirstMetaClass元类创建的类,会额外添加name属性和say()方法。
class CLanguage(object, metaclass=FirstMetaClass):
pass
if __name__ == '__main__':
clangs = CLanguage()
print(clangs.name)
clangs.say()
可以看到,在创建类时,通过元类创建的类会调用元类中的__new__方法,从而实现动态修改类属性或者类方法的目的。
3、元类创建API实例---Django ORM
class ModelMetaClass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
for k, v in attrs.items():
# 只处理类中属性值为元组的键值对
if isinstance(v, tuple):
print('Found mapping: %s==>%s' % (k, v))
mappings[k] = v
# 处理完成后删除属性
for k in mappings:
attrs.pop(k)
attrs["__mappings__"] = mappings
attrs["__table__"] = name
# 这里不能用type(name, bases, attrs) 直接返回创建好的类, 不会触发type的__init__方法
return type.__new__(cls, name, bases, attrs)
class Model(metaclass=ModelMetaClass):
# uid = ("uid", "int unsigned")
# name = ("username", "varchar(30)")
# email = ("email", "varchar(30)")
# password = ("password", "varchar(30)")
def __init__(self, **kwargs):
# 将关键字参数添加到实例属性
for k, v in kwargs.items():
setattr(self, k, v)
def save(self):
table_name = self.__table__
field = list()
args = list()
for k, v in self.__mappings__.items():
field.append(v[0])
arg = getattr(self, k, None)
# ["12345","""'Michael'""","""'test@orm.org'""","""'my-pwd'"""]
if isinstance(arg, str):
args.append("""'{}'""".format(arg))
else:
args.append(str(arg))
sql = "insert into {}({}) values ({})".format(table_name, ",".join(field), ",".join(args))
print(f"SQL语句: {sql}")
class User(Model):
uid = ("uid", "int unsigned")
name = ("username", "varchar(30)")
email = ("email", "varchar(30)")
password = ("password", "varchar(30)")
u = User(uid=12345, name="Michael", email="test@orm.org", password="my-pwd")
u.save()