Python元类__prepare__方法深入理解
学习元类的时候,对__prepare__不是很理解,书上讲解的也不是很详细,最后通过查看stackoverflow的一些帖子对该方法有了一些理解,记录如下:
先看代码:
class member_table(dict):
def __init__(self):
self.member_names = []
def __setitem__(self, key, value):
if key not in self:
self.member_names.append(key)
dict.__setitem__(self, key, value)
class OrderedClass(type):
@classmethod
def __prepare__(metacls, name, bases):
classdict = member_table()
print("prepare return dict id is:", id(classdict))
return classdict
def __new__(metacls, name, bases, classdict):
print("new get dict id is:", id(classdict))
result = type.__new__(metacls, name, bases, dict(classdict))
result.member_names = classdict.member_names
print("the class's __dict__ id is:", id(result.__dict__))
return result
def __init__(cls, name, bases, classdict):
print("init get dict id is ", id(classdict))
super().__init__(name, bases, classdict)
class MyClass(metaclass=OrderedClass):
def method1(self):
pass
def method2(self):
pass
print("MyClass locals() id is ", id(locals()))
输出为:
prepare return dict id is: 2093480992528
MyClass locals() id is 2093480992528
new get dict id is: 2093480992528
the class's __dict__ id is: 2093482830200
init get dict id is 2093480992528
可见,执行顺序为:
prepare(创建命名空间)-> 依次执行类定义语句 -> new(创建类)-> init(初始化类)
元类定义了prepare以后,会最先执行prepare方法,返回一个空的定制的字典,然后再执行类的语句,类中定义的各种属性被收集入定制的字典,最后传给new和init方法。
再来看其它输出:
MyClass.member_names
['__module__', '__qualname__', 'method1', 'method2']
MyClass.attr1 = 'attr1'
MyClass.__dict__
mappingproxy({'__dict__': <attribute '__dict__' of 'MyClass' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
'attr1': 'attr1',
'member_names': ['__module__',
'__qualname__',
'method1',
'method2'],
'method1': <function __main__.MyClass.method1>,
'method2': <function __main__.MyClass.method2>})
id(MyClass.__dict__)
2093482829864
MyClass.member_names
['__module__', '__qualname__', 'method1', 'method2']
上面的例子,在new方法中,dict被替换成一个普通的dict。所以MyClass.member_names不会记录class创建以后新增的属性。同时__dict__属性是类命名空间的一个代理,每次查看其id都不同。
3.6版本以前,prepare方法主要用来返回一个orderdict对象,以保存类中属性的添加顺序。而3.6版本以后,默认已经是保持顺序的了。
stackoverflow上的讨论帖
平时学习的一些笔记正在慢慢上传至github,欢迎大家交流。
地址是:https://github.com/telecomshy/Python-Private-Manual