元类
元类
1、什么是元类
创建出类的类,就是元类。例如:type就是元类
2、如何产生类
产生类的方法有两种:
第一种:通过class关键字产生类
# 1.通过class关键字产生类 class Chinese(object): country = 'China' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex p_obj = Chinese("张三", 87, "male") print(type(p_obj)) # <class '__main__.Chinese'> # 类本质上也是一个对象,因为在python中 一切皆对象 print(Chinese) # <class '__main__.Chinese'>
第二种:通过type元类产生类 type()
# 2.通过调用type产生类:type() code = """ country = 'China' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex """ class_attr = {} # 定义一个类的名称空间class_attr # exec()是python的内置函数,可以将字符串的代码添加到名称空间中,名称空间可以自定义; # 语法exec(字符串形式的代码,全局名称空间,局部名称空间) exec(code, {}, class_attr) # 将code中的代码添加到全局和局部名称空间中
# type()需要传入的参数:what, bases=None, dict=None # what:在这里指的是类名 bases:继承的父类,也就是基类 dict:类的名称空间 obj = type("Chinese", (object, ), class_attr) # 产生一个Chinese类的对象obj print(obj) # <class '__main__.Chinese'>
3、元类的作用
元类可以控制类的创建过程
例:
先创建一个元类,然后要求根据元类创建的类的类名字必须首字母大写而且类内部必须要有注释
# 自定义一个元类, 元类在此是“控制类” class MyMetaClass(type): # 继承type类,然后重写type类中的方法来创建类 # 重写type类的__init__方法 # 将type()中需要传入的三个参数:what, bases=None, dict=None 改为 class_name, class_bases, class_dict (这里仅仅只是改了个名字而已) def __init__(self, class_name, class_bases, class_dict): # print(class_name) # print(class_bases) # print(class_dict) if not class_name.istitle(): # .istitle():判断首字母是否是大写 raise NameError("类的名字首字母必须大写!!!") # 抛出异常 if not class_dict.get("__doc__"): # 判断类的名称空间中是否有__doc__属性,即“注释” raise TypeError("类必须写注释!!!") # 抛出异常 # 必须将类中的类名、类的基类、类的名称空间,一并返回给type中的__init__ super().__init__(class_name, class_bases, class_dict) # 以下为调用类时,类内部如何产生空对象以及对象名称空间的原理(不必懂): # __call__控制调用类的行为 # 调用类时type内部一定会调用一次__call__,由__call__来帮你调用__new__创建空对象,然后__call__再调用__init__将类中传入的参数放入对象的名称空间中 def __call__(self, *args, **kwargs): # 重写type类的__call__方法 print(args) # 打印的args为调用类传入的参数 # 调用object来创建一个空对象obj obj = object.__new__(self) # 调用类时,__call__会立马调用 类名.__init__,并且将obj连同User括号内的参数一同传给__init__,然后产生名称空间 self.__init__(obj, *args, **kwargs) return obj
# 自定义一个类,此类在此时是新创建的MyMetaClass元类的被控制类 # 被控制类在定义阶段的 "metaclass=自定义的元类"参数,他会将当前被控制类的名称、类的基类、类的名称空间一并传给自定义的元类(上面创建的MyMetaClass元类) # metaclass=自定义的元类,自定义的元类继承了type元类,那么他就会去调用type元类(名称、类的基类、类的名称空间) class User(object, metaclass=MyMetaClass): # 被控制类,MyMetaClass(User, (object, ), {"x": 10}) """y is very handsome~""" x = 10 pass
# obj = User() # 此处调用被控制类不传参,所以上面打印出的args参数为空,即 ()
此例的需求是:控制创建类的类名字必须首字母大写而且类内部必须要有注释,下面开始演示(上面为按照元类的需求来写,下面开始演示不按照需求来写)
演示一:被控制类名字首字母小写
执行结果:
演示二:被控制类中不写注释
执行结果: