首页 > 技术文章 > Python元类__prepare__方法深入理解

telecomshy 2018-06-26 11:29 原文

学习元类的时候,对__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

推荐阅读