为什么类会拥有其元类的属性?
最近在学习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类创造出来,所以本质上说实例和类是不同的东西,类有继承链,实例没有,只有自身的命名空间。所以对于实例来说,先在自己的命名空间里面查找属性,找不到,再在其类的继承链里寻找属性。
如果有不对的地方,请大家指出。