在汉语中,元有本源、根本的意思!Python中,元类即可以通过实例化产生类的类,通俗来讲它就是类的祖师爷!Python内置的元类是type,我们用class关键字定义的类都是type实例化后得到的对象!因为Python一切皆对象,所以类本身也是对象,也可以通过实例化产生!
我们用class定义类的时候,Python都帮我们干了些什么呢?下面通过一步步的分析重现底层操作!
一、exec补充
exec函数能够执行储存在字符串或文件中的Python语句,它会将执行期间产生的名字存放于局部名称空间中,语法如下:
exec = (__source, __globals, __locals)
1.1、参数一:source
包含有一系列Python代码的字符串
1.2、参数二:globals
全局作用域(字典形式),如果不指定,默认为globals()
1.3、参数三:locals
局部作用域(字典形式),如果不指定,默认为locals()
二、class定义类的步骤
>>> class Chinese:
... def __init__(self, name):
... self.name = name
...
>>> gd = Chinese('sanxi')
2.1、定义类名
譬如:class_name='Chinese'
2.2、继承基类
譬如:class_bases=object
,不指定则Python默认继承object!
2.3、开辟名称空间
-
类名名称空间:
class_dict = {}
-
类体代码:
class_body = '' def __init__(self, name):
... self.name = name''
-
执行代码得到名称空间:
class_dict = exec(class_body, {}, class_dict)
2.4、调用元类
Chinese = type(class_name, class_bases, class_dict)
三、自定义元类
Python中,不指定则所有类默认的元类是type!我们可以通过继承type来自定义元类,然后使用metaclass来为一个类指定元类!
3.1、调用自定义元类步骤:
3.1.1、调用__new__造空对象
object下默认有一个new方法,我们可使用它来创造空对象而不用特意去定义new方法!还记得之前说过Python3的类默认都继承object吗?按照名称查找顺序,类内没有会一级一级往上找,直至找到object的new方法!
3.1.2、调用Mymeta的init初始化对象
下面这个super其实是type的init方法,因为new方法先于init执行,因此也包含了new!
super(Mymeta, cls).__init__(class_name, class_bases, class_namesapce)
3.1.3、返回初始化好的对象
def __call__(cls, *args, **kwargs): # 因为类是可以实例化产生对象的,所以必须是可调用的;__call__方法就是使对象变得可调用
if args or kwargs: # 有值时
obj = object.__new__(cls) # 造空对象
cls.__init__(obj, args, kwargs) # 初始化空对象
return obj # 返回对象
3.2、完整示例:
class Mymeta(type): # 继承了type的类才能称之为自定义元类
def __init__(cls, class_name, class_bases, class_namesapce): # init控制普通类Chinese的创建
# self是 <class '__main__.Chinese'>
# class_bases是 (<class 'object'>,)
# class_dic是Chinese的名称空间
super(Mymeta, cls).__init__(class_name, class_bases, class_namesapce) # 重用父类功能
if class_name.islower(): # 自定义功能
raise TypeError(f'类名:{class_name} 必须为驼峰体')
if '__doc__' not in class_namesapce or len(class_namesapce['__doc__'].strip(' \n')) == 0:
raise TypeError(f'类:{class_name} 必须包含注释且不能为空')
def __call__(cls, *args, **kwargs): # 控制类Chinese的调用过程
if args or kwargs:
obj = object.__new__(cls)
cls.__init__(obj, args, kwargs)
return obj
class Chinses(object, metaclass=Mymeta): # 新类指定Mymeta为元类
"""
注释信息
"""
country = 'China'
def __init__(self, name, age):
self.name = name
self.age = age
gd = Chinses('sanxi', 18)
print(type(Chinses))
# 执行得到结果
<class '__main__.Mymeta'>