印象中,是在建立单例模式时知道能够用到元类(metaclass),但始终对其了解的不是很透彻,不少人也都说元类是Python中较难理解的概念之一,因而找来几本书,但愿能够找到答案,本文以Python3为例。python
本文参考:设计模式
《人人都懂设计模式》函数
《Python Cookbook》设计
《 流畅的Python》code
先来简单介绍下:元类(metaclass)是一个类,你也能够理解为类的类,由于Python中的类是在运行时动态建立的,那么经过元类即可以控制类属性和类实例的建立过程。对象
来看看用元类实现的单例模式:blog
class Singleton(type): """ 单例模式 """ def __init__(cls, *args, **kwargs): cls.__instance = None super().__init__(*args, **kwargs) def __call__(cls, *args, **kwargs): if cls.__instance is None: cls.__instance = super().__call__(*args, **kwargs) return cls.__instance class Test(metaclass=Singleton): def __init__(self): pass a = Test() b = Test() print(id(a)) print(id(b))
具体的实现是:建立类时显式的指定类的metaclass,而自定义的metaclass继承type,并从新实现__call__
方法。继承
因而,有了两个问题:源码
由于,在Python中,type是默认的metaclass(内建元类),Python容许咱们自定义metaclass,自定义的metaclass必须继承自type,也就是:元类从type类继承了构建类的能力。it
咱们一般用type来获取对象所属的类,就像这样:
In [10]: a = 10 In [11]: type(a) Out[11]: int
然而,type仍是一个类,你能够经过type来新建一个类,看type的源码,经过type(name, bases, dict)
即可以生成一个新的类:
In [44]: test_class = type('Test', (), dict({'name': None})) In [45]: a = test_class() In [46]: a.name = 'Tony' In [47]: a.name Out[47]: 'Tony'
默认状况下,Python中类都是type类的实例:
In [12]: class A: ...: pass ...: In [13]: A.__class__ Out[13]: type In [14]: int.__class__ Out[14]: type
当你使用class关键字时,Python在幕后作的事情,就是经过元类来实现的。
__call__
方法?提出该问题是由于,与Python类建立相关的方法是:
__new__
:类方法,负责对象的建立,在定义类时须要返回一个实例,在咱们经过类名进行实例化对象时自动调用。
__init__
:初始化函数,负责对new实例化的对象进行初始化,负责对象状态的更新和属性的设置,在每一次实例化对象以后调用。
而咱们经常使用__call__
方法只是为了声明这个类的对象是可调用的(callable)。
可是,在metaclass中__call__
方法还负责对象的建立,这就是为何要从新定义的缘由了。
重定义了__call__
方法以后,一个对象的建立过程大概以下图:
咱们验证一下:
class TestMetaClass(type): def __init__(cls, what, bases=None, dict=None): print("metaclass init") super().__init__(what, bases, dict) def __call__(cls, *args, **kwargs): print("metaclass call") self = super(TestMetaClass, cls).__call__(*args, **kwargs) return self class TestClass(metaclass=TestMetaClass): def __init__(self, *args, **kwargs): print("class init") super().__init__() def __new__(cls, *args, **kwargs): print("class new") self = super().__new__(cls) return self a = TestClass()
返回:
metaclass init metaclass call class new class init
能够看到,__call__
方法在类执行__new__
和__init__
以前执行,这样就能够解释:
在Singleton中的__call__
方法对类属性__instance
进行判断:
__instance
为None,代表类还未进行实例化,那么给__instance
赋值为元类的父类(type)的__call__
方法。__instance
不为None,说明类已经进行过实例化,直接返回cls.__instance中的类实例。便实现了单例模式。
除了从新定义__call__
之外,元类能够经过实现__init__
方法来定制实例,元类的__init__
方法能够作到类装饰器能作到的任务事情,而且做用更大。
若是想要进一步定制类,能够在元类中实现__new__
方法。
另,编写元类时,一般会把self参数改成cls,这样能更清楚的代表要构建的实例是类。
元类的调用
上述例子中,都是经过metaclass=''
来设置类的元类,还能够这样:
class TestClass(): __metaclass__ = TestMetaClass def __init__(self, *args, **kwargs): print("class init") super().__init__()
在执行类定义时,解释器会先寻找这个类属性中的__metaclass__
,若是此属性存在,就将这个属性赋值给此类做为它的元类,若是此属性没有定义的话,就会向上查找父类的__metaclass__
,若是没有发现任何的父类,而且解释器中也没有名字为__metaclass__
的全局变量,这个类就是传统类,会使用type.ClassType做为此类的元类。
以上。