最近在学习Python的一些设计模式,当看到用元类实现单例模式代码的时候,发现一个很有意思的问题,先看代码:
class Meta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = type.__call__(cls, *args, **kwargs) return cls._instances[cls]问题是_instances不是元类的属性吗?为什么可以通过cls._instances来访问?
stackoverflow有人曾提过相同的问题,点这里可以看。
具体的实现细节没有完全弄清楚。编写了一些实验的代码,如下:
class TopMeta(type): topmeta = 'topmeta' class MetaBaseCls: metabasecls = 'metabasecls' class UpperMeta(type, MetaBaseCls, metaclass=TopMeta): uppermeta = 'uppermeta' attr = 'uppermeta attr' class BaseCls: basecls='basecls' attr = 'basecls attr' class C(BaseCls, metaclass=UpperMeta): pass C.basecls C.uppermeta C.metabasecls C.attr try: print(C.topmeta) except Exception as e: print(e)输出为:
'basecls' 'uppermeta' 'metabasecls' 'basecls attr' type object 'C' has no attribute 'topmeta'根据上面的实验,可以发现:
C会先在自身的继承链里查找,所以打印basecls attr,而不是uppermeta attr。
uppermeta和metabasecls都在其元类的继承链中,所以都可以找到。
topmeta是其元类的元类,并不在其元类的继承链中,所以找不到,会报错。
简单总结一些规律:
对象(注意这个对象包含实例和类)会先在自己的整个继承链里面寻找属性,如果没有找到,则会在它的类的继承链里面寻找属性,而类是元类的实例,换句话说,类的类是元类。所以类会先在自身的继承链里面查找属性,如果找不到,则到它的元类的继承链里面查找属性。
这里容易想不通的是按照以上解释,实例的继承链不就是其类的继承链吗?那不是重复了?
个人是这样理解,实例是通过object类创造出来,类是通过type类创造出来,所以本质上说实例和类是不同的东西,类有继承链,实例没有,只有自身的命名空间。所以对于实例来说,先在自己的命名空间里面查找属性,找不到,再在其类的继承链里寻找属性。
如果有不对的地方,请大家指出。