27. 元类
一、什么是元类
在 Python 中,一切皆为对象,即类也是一个对象。type 是内置的元类。我们用 class 关键字定义的所有的类以及内置的类都是由元类 type(内置的元类) 实例化产生的。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def show_info(self):
print(f"name: {self.name}, age: {self.age}")
print(type(Person))
print(type(int))
当 Python 解释器遇到 class 的时候,实际上会进行调用,其目的是:至少要知道有哪些属性,有哪些方法,然后将这些类属性、方法传递到元类 type 中,进行创建一个对象,这个对象就是我们所谓的类对象。
二、class机制分析
# 1、类名
class_name = "Person"
# 2、类的基类
class_base = (object,)
# 3、执行类体代码拿到类的名称空间
class_dict = {"name": "unknown"}
class_body = """
def __init__(self, name, age):
self.name = name
self.age = age
def show_info(self):
print(f"name: {self.name}, age: {self.age}")
"""
exec(class_body, {}, class_dict)
print(class_dict)
# 4、调用元类
Person = type(class_name, class_base, class_dict)
print(Person)
# 5、创建对象
p = Person("Sakura", 10)
p.show_info()
# 1、类名
class_name = "Person"
# 2、类的基类
class_base = (object,)
# 3、执行类体代码拿到类的名称空间
def show_info(self):
print(f"name: {self.name}")
@classmethod
def description(cls):
print(f"{cls.name}是一个人")
@staticmethod
def static():
print("我是一个静态方法")
class_dict = {"name": "unknown", "show_info": show_info, "description": description, "static": static}
# 4、调用元类
Person = type(class_name, class_base, class_dict)
print(Person)
# 5、创建对象
p = Person()
p.show_info()
p.description()
p.static()
三、自定义元类
# 只有继承了type类的类才是元类
class MyMeta(type):
# cls当前所在的类,*args、**kwargs调用类时所传入的参数
def __new__(cls,*args,**kwargs):
print("__new__()方法运行了")
print(cls)
print(args)
print(kwargs)
return type.__new__(cls, *args, **kwargs)
# __init__()接收的参数是__new__()方法返回的
def __init__(self, class_name, class_bases, class_dict):
print("__init__()方法运行了")
print(self)
print(class_name)
print(class_bases)
print(class_dict)
def __call__(self,*args,**kwargs):
print(self)
print(args)
print(kwargs)
# MyMeta.__call__()方法会先调用Person类内的__new__()方法
obj = self.__new__(self)
# MyMeta.__call__()方法内会调用Person类内的__init__()方法
self.__init__(obj,*args,**kwargs)
# MyMeta.__call__()方法会返回一个初始化好的对象
return obj
"""
调用MyMeta发生的三件事:
1、先造一个空对象,调用类内的__new__()方法,就是调用type.__call__()方法
2、调用MyMeta这个类内的__init__()方法,完成初始化对象的操作
3、返回初始化好的对象
Person = MyMeta(class_name,class_bases,class_dict)
"""
"""
类的产生:Person = MyMeta() --> type.__call__()干的三件事:
1、type.__call__()方法内会先调用MyMeta类内的__new__()方法
2、type.__call__()方法内会调用MyMeta类内的__init__()方法
3、type.__call__()方法会返回一个初始化好的对象
"""
class Person(metaclass=MyMeta):
def __new__(cls,*args,**kwargs):
# 产生真正的对象
return object.__new__(cls)
def __init__(self,name,age):
self.name = name
self.age = age
def show_info(self):
print(f"name: {self.name}, age: {self.age}")
"""
类的调用:p = Person("Sakura",10) --> MyMeta.__call__()干的三件是
1、MyMeta.__call__()方法内会先调用Person类内的__new__()方法
2、MyMeta.__call__()方法内会调用Person类内的__init__()方法
3、MyMeta.__call__()方法会返回一个初始化好的对象
"""
p = Person("Sakura",10)
print(p)
print(p.__dict__)
# 只有继承了type类的类才是元类
class MyMeta(type):
def __new__(cls, class_name, class_base, class_dict):
print("__new__()方法运行了")
# 方法1:通过type来做类对象的创建
return type(class_name, class_base, class_dict)
# 方法2:复用type.__init__()方法
#return type.__new__(cls, class_name, class_base, class_dict)
class Person(metaclass=MyMeta):
def __init__(self,name,age):
self.name = name
self.age = age
def show_info(self):
print(f"name: {self.name}, age: {self.age}")
p = Person("Sakura",10)
print(p)
print(p.__dict__)
只要调类,那么会依次调用类内的
__new__()
方法,在调用__init__()
方法;如果想要一个对象可以加括号调用,需要在该对象的类中添加一个
__call__()
方法;
四、利用元类实现单例模式
class MyType(type):
def __init__(self, name, base, attrs):
super().__init__(name, base, attrs)
self.instance = None
def __call__(self, *args, **kwargs):
# 判断是否有对象,有,则不创建,没有,则创建
if not self.instance:
# 调用自己的那个类的__new__()创建对象
self.instance = self.__new__(self)
# 调用自己的那个类的__init__()初始化对象
self.__init__(self.instance, *args, **kwargs)
return self.instance
class P(metaclass=MyType):
pass
p1 = P()
print(p1)
p2 = P()
print(p1)