众所周知,在Python编程中,经过class定义类,再经过类实例化生成实例对象,全部实例对象都继承自object对象。但其实class自己也是一个对象,咱们称之为类对象,全部class对象继承自type。
咱们经过如下简单代码在Python交互式CLI中进行测试:html
# 定义类A >>> class A(object): >>> pass >>> type(A) <class 'type'> >>> type(A()) <class '__main__.A'> >>> isinstance(A, type) True >>> isinstance(A(), object) True
咱们能够经过类(class)对实例对象(instance object)进行定制和设计,控制实例对象的建立过程,那么咱们是否能经过type控制类对象(class object)的建立过程,从而对类对象的建立过程进行定制和设计呢?答案是可定的。python
type自己也是一个类(class),它不但能判断一个对象的类型,还能够建立类对象。sql
三个参数描述以下:数据库
经过type建立类示例:django
# 定义一个方法 def greeting(self, name='world'): print("Hello, %s." % name) # 经过type建立类 Hello = type("Hello", (object,), {"greeting": greeting}) h = Hello() h.greeting() # >> Hello, world. print(type(Hello)) #>> <class 'type'> print(type(h)) # >> <class '__main__.Hello'>
不但能够经过type动态的建立类,还能够经过继承type
建立一个元类Metaclass,把此Metaclass类做为其余类的metaclass。class是实例对象(instance)的模板,而Metaclass则是class的模板,三者间的关系描述以下图。编程
+----------+ +----------+ +----------+ | | | | | | | | instance of | | instance of | | | instance +------------>+ class +------------>+ metaclass| | | | | | | | | | | | | +----------+ +----------+ +----------+
经过这种方式,在建立类对象前,Python先会执行Metaclass的相关方法来定制类对象,从而达到对类进行动态定制的目的,Django中的ORM框架,就是经过这种方式来实现的。这种定制,包括给类增长属性、方法,对类进行二次处理等。app
下面演示经过Metaclass给自定义list类增长insert_before
方法的过程。框架
class ListMetaclass(type): def __new__(mcs, name, bases, attrs): attrs["insert_before"] = lambda self, item: self.insert(0, item) return type.__new__(mcs, name, bases, attrs) class NewList(list, metaclass=ListMetaclass): pass new_list = NewList(["a", "b"]) new_list.insert_before("insert_before") print(new_list) # >> ['insert_before', 'a', 'b']
__new__
方法是用来建立实例对象的,相似的Metaclass的__new__方法是用来建立类对象的。在建立类对象前,咱们经过在attrs中增长insert_before属性,这样建立的类对象中就拥有此属性。
注:
Metaclass.__new__
的参数说明见上述type的参数说明。
下面来看一个更实际可用的例子,以Django Tutorial中的展现的ORM的使用方式为例,经过Metaclass来开发一个简易的ORM框架。函数
Metaclass的寻找顺序:
类建立时,先查找类自己是否设置了Metaclass,若是设置了,则直接调用Metaclass建立类;若是没有,则根据基础顺序,一路找到基类,若是找到,则调用父类的Metaclass建立类;若是都没找到,则调用type建立类。
Django Tutorial使用ORM主要包括三个步骤:测试
示例代码以下:
# 定义模型类 class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') # 建立模型对象 q = Question(question_text="What's new?", pub_date=timezone.now()) # 保存模型数据到数据库 q.save()
ORM框架的基本设计思路包括以下几点:
先搭个框架,再填充各部分功能。
from datetime import datetime class ModelMetaClass(type): def __new__(mcs, name, bases, attrs): return type.__new__(mcs, name, bases, attrs) class Model(metaclass=ModelMetaClass): def __init__(self, **kwargs): pass def __setattr__(self, key, value): pass def __getattr__(self, item): pass def save(self): pass class Field: pass class CharField(Field): pass class DateTimeField(Field): pass class Question(Model): question_text = CharField() pub_date = DateTimeField() question = Question(question_text="My first question.", pub_data=datetime.now()) question.save()
根据设计思路,功能完善后的代码以下:
from datetime import datetime class ModelMetaclass(type): def __new__(mcs, name, bases, attrs): # 只处理Model的子类 if name == "Model" or Model not in bases: return type.__new__(mcs, name, bases, attrs) # 处理Field类型字段,信息保存在__fields__字段中 fields = dict() for key in attrs: if isinstance(attrs[key], Field): fields[key] = attrs[key] attrs["__fields__"] = fields # 表名默认为class名的小写 attrs["__table__"] = name.lower() # 删除Field类型的类变量 for key in fields: attrs.pop(key) return type.__new__(mcs, name, bases, attrs)
Model类做为全部ORM模型的基类,为模型提供通用功能。
__init__
为模型构造函数,处理传入的Field字段__setattr__
、__getattr__
让模型Field类型字段能够像普通字段同样进行设值和取值save
方法用于产生把模型保存到数据库的SQL语句class Model(metaclass=ModelMetaclass): def __init__(self, **kwargs): # Field字段数据保存在self.fields中 self.fields = dict() # self.__fields__即Metaclass中的attrs["__fields__"]字段 model_fields = self.__fields__ for kwarg_key in kwargs: if kwarg_key in model_fields: self.fields[kwarg_key] = kwargs[kwarg_key] else: raise KeyError() def __setattr__(self, key, value): # 实现经过model.field = xxx 对Field字段赋值 if key in self.__fields__: self.__dict__["fields"][key] = value return self.__dict__[key] = value def __getattr__(self, key): # 实现经过model.field 读取Field字段值 if key in self.__fields__: return self.__dict__["fields"][key] return self.__dict__[key] def save(self): model_fields = self.__fields__ fields_key = list() fields_value = list() fields_placeholder = list() for field_key in model_fields: fields_key.append(field_key) fields_value.append(self.fields.setdefault(field_key, None)) fields_placeholder.append("?") sql = "INSERT INTO %s (%s) VALUES (%s)" % (self.__table__, ", ".join(fields_key), ", ".join(fields_placeholder)) print("SQL: ", sql) print("ARGS: ", fields_value)
class Field: pass class CharField(Field): pass class DateTimeField(Field): pass class Question(Model): question_text = CharField() pub_date = DateTimeField()
question = Question(question_text="My first question.", pub_date=datetime.now()) question.save() # >> SQL: INSERT INTO question (question_text, pub_date) VALUES (?, ?) # >> ARGS: ['My first question.', datetime.datetime(2020, 10, 13, 17, 52, 38, 443969)] question.question_text = "My second question." question.save() # >> SQL: INSERT INTO question (question_text, pub_date) VALUES (?, ?) # >> ARGS: ['My second question.', datetime.datetime(2020, 10, 13, 17, 52, 38, 443969)]
从打印输出可用看到,框架已经打印出了SQL语句和参数,只要提交到数据库就能够实际运行了。
经过以上几个简单的示例,相信你们对Metaclass的使用应该有了一个更深的了解,不过要理解代码,最好仍是本身动手编码、调试,纸上得来终觉浅,绝知此事要躬行。
得益于Python的动态语言特性的灵活性和精巧设计,咱们经过不到100行代码,就实现了一个ORM框架原型,和Java等重量级编译型语言相比,各方面都要更胜一筹。
不过这只是一个简易的示例,实际框架还有不少的工做要作。
参考资料: