1、简介:
metaclass
翻译为元类,字面理解“元”是本源,它可以控制类的创建行为。
定义上很难理解它的意思,现在开始举例子说明它的用法。
2、原理:
-
python中用户自定义的类,实际上是type这个类的实例,接下来用代码说明:
class MyClass(object): pass instance = MyClass() print(type(instance)) print(type(MyClass)) # 输出 <class '__main__.MyClass'> <class 'type'>
可以看到,
instance
是MyClass
的实例,而MyClass
是type
的实例。 -
用户创建类实例时,其实是调用了
type
的__call__
方法。创建这样一个类:
class MyClass: data = 1
实际上执行的是下面这段代码:
MyClass = type('MyClass', (), {'data': 1})
看下实际的执行:
class MyClass: data = 1 instance = MyClass() print(instance) # 输出 <__main__.MyClass object at 0x000002710DB8D608> MyClass = type('MyClass', (), {'data': 1}) instance = MyClass() print(instance) # 输出 <__main__.MyClass object at 0x000002710DB8D608>
-
metaclass
(此处为自己定义的元类)是type
的子类,可以通过重载type
的__call__
方法,来控制类的创建,使其不再是普通的类。MyClass = type('MyClass', (), {'data': 1})
就变成了(
MyMeta
需要自己定义,必须是type
的子类):MyClass = MyMeta('MyClass', (), {'data': 1})
-
举个例子:
class MyMeta(type): def __new__(cls, *args, **kwargs): print("MyMeta------__new__") return type.__new__(cls, *args, **kwargs) def __init__(cls, name, bases, dic): print("MyMeta------__init__") super().__init__(name, bases, dic) def __call__(cls, *args, **kwargs): print("MyMeta------__call__") return type.__call__(cls, *args, **kwargs) class MyClass(metaclass=MyMeta): data = 1 instance = MyClass() # 打印 MyMeta------__new__ MyMeta------__init__ MyMeta------__call__
可以看到,创建类的时候,就会自动执行对应元类的
__new__
和__init__
,创建对应类实例时,就会执行对应的__call__
。
3、实际应用:
-
设计模式--单例模式:
class Single_instance(type): def __init__(self, name, bases, dic): self.__instance = None super().__init__(name, bases, dic) def __call__(self, *args, **kwargs): if not self.__instance: self.__instance = super().__call__(*args, **kwargs) return self.__instance class MyClass(metaclass=Single_instance): pass c1 = MyClass() c2 = MyClass() print(c1) print(c2) # 打印 <__main__.MyClass object at 0x0000016856510EC8> <__main__.MyClass object at 0x0000016856510EC8>
-
import yaml class People(yaml.YAMLObject): yaml_tag = u'!Monster' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def __repr__(self): return f"{self.__class__.__name__}(name={self.name}, age={self.age}, sex={self.sex})" p1 = yaml.full_load(""" !Monster name: ZhangSan age: 16 sex: boy """) print(type(p1)) print(p1) # 打印 <class '__main__.People'> People(name=ZhangSan, age=16, sex=boy) p2 = People("LiSi", 14, "boy") print(p2) print(yaml.dump(p2)) # 打印 People(name=LiSi, age=14, sex=boy) !Monster age: 14 name: LiSi sex: boy
YAMLObject
便是使用YAMLObjectMetaclass
(自定义元类)创建的类,该功能称为序列化/反序列化,调用yaml.yaml.full_load()
就能把一个yaml
格式的内容加载成一个object,而yaml.dump()
就能把一个YAMLObject
子类序列化。这样在只需要修改
yaml
的配置文件,就可以动态修改想要创建的实例。
4、总结:
metaclass
的功能很强大,可以修改类的创建过程,实现单例、缓存以及上面的序列化/反序列化等的功能,同样如果使用不慎,会出现很大的问题。