1. 元类是什么
众所周知,对象由类实例化而来,类是对象的模板,而python一切皆对象,类也是对象,它由元类(type)创建,所以元类是类的类,是类的模板
2. 创建类的另一种方法
一般情况下,我们使用class关键字申明一个类,就像
class Demo:
def __init__(self,name,age):
self.name = name
self.age = age
def output(self):
print("name is " + str(self.name) + " age is " + str(self.age))
if __name__ == '__main__':
demo = Demo("Bob",18)
demo.output()
python 中所有的类都是通过type创建的,所以当我们使用type()函数查看类的类型时会显式<class type>
>>> class Demo:
pass
>>> type(Demo)
<class 'type'>
>>> demo = Demo()
>>> type(demo)
<class '__main__.Demo'>
通过类实例化出来的对象的类型是
<class 类名>
,这样也更加验证了所有类都是由元类type实例化而来
通过type创建类
可以看一下type的文档,type可以传入三个参数,object_or_name, bases, dict,当只有一个参数是object时,返回该对象的类型,就是最常使用的这种情况,当传入name, bases, dict参数时,会返回一个类,name是类名,bases是基类元组,dict是类中属性和方法的字典
class type(object):
"""
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
"""
我们使用type重写一下上面的Demo类
# 模拟__init__()
def __init__(self,name,age):
self.name = name
self.age = age
def output(self):
print("name is " + str(self.name) + " age is " + str(self.age))
class_name = 'Demo'
class_bases = (object,)
class_dict = {
'__init__':__init__,
'output': output,
}
# type(name, bases, dict) -> a new type
Demo = type(class_name,class_bases,class_dict)
demo = Demo('Bob',18)
demo.output() # name is Bob age is 18
实际上,每次用class定义类时,执行的都是type()方法
3. MetaClass
既然所有类都是由type创建的,那我们就可以控制类的创建行为,这就需要使用元类metaclass
- 元类用来创建类,实质上也是一个类,继承自type
-
__new__
是真正的构造函数,用来分配内存空间__new__(cls: type, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> type
def add(self, value):
print('add one value')
self.append(value)
class ListMetaclass(type):
def __new__(mcs, name, bases, namespace):
namespace['add'] = add
return type.__new__(mcs, name, bases, namespace)
class MyList(list, metaclass=ListMetaclass):
pass
li = MyList()
li.add(1)
print(li)
在用class定义类时,括号中可以指定metaclass,指定后会创建__metaclass__
,python在创建类的时候,会先检查有没有__metaclass__
,如果有,就会以此方法创建对象,没有就会逐级向上查找父类中有没有该,如果找到当前package中还没有找到,就会使用默认的type创建(调用metaclass.__new__()
)
值得注意的是,如果我们在做类的定义时,在class声明处传入关键字metaclass=ListMetaclass,那么如果传入的这个metaclass有__call__函数,这个__call__函数将会覆盖掉MyList class的__new__函数。这是为什么呢?请大家回想一下,当我们实例化MyList的时候,用的语句是L1=MyList(),而我们知道,__call__函数的作用是能让类实例化后的对象能够像函数一样被调用。也就是说MyList是ListMetaclass实例化后的对象,而MyList()调用的就是ListMetaclass的__call__函数。另外,值得一提的是,如果class声明处,我们是让MyList继承ListMetaclass,那么ListMetaclass的__call__函数将不会覆盖掉MyList的__new__函数。
元类在一般情景下很少用到,但在像ORM中还是会有应用的,ORM(对象关系映射),ORM看这位大佬的文章谈谈Python中元类Metaclass(二):ORM实践
4. 总结
- 通过class定义的类其实是通过type()创建的
- type(object_or_name, bases, dict)
- 如果想要控制类的创建行为,需要在创建类时指定metaclass,一旦指定了metaclass,就会在class上添加
__metaclass__
,创建类时会找__metaclass__
指向的类,并用这个类创建类,如果找不到,就会调用默认的type()
参考文章
Python中的元类(metaclass)谈谈Python中元类Metaclass(一):什么是元类Python之元类