美文网首页
python 对象创建过程

python 对象创建过程

作者: lmem | 来源:发表于2017-04-11 10:05 被阅读111次

    http://eli.thegreenplace.net/2012/04/16/python-object-creation-sequence/
    http://blog.xiayf.cn/2012/04/26/python-object-creation-sequence/
    Python把它看作对可调用的Joe的一次调用,并且将它路由到内部函数 PyObject_Call ,将Joe作为PyObject_Call的第一个参数。 PyObject_Call 根据其第一个参数的类型抽取这个参数类型的 tp_call 属性。

    class Joe:
        pass
    
    j = Joe()
    

    换句话说,就是调用 Objects/typeobject.c 文件中的函数 type_call

    static PyObject *
    type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
    {
        PyObject *obj;
    
        if (type->to_new == NULL) {
            PyErr_Format(PyExc_TypeError,
                        "cannot create '%.100s' instances",
                        type->tp_name);
            return NULL;
        }
    
        obj = type->tp_new(type, args, kwds);
        if (obj != NULL) {
            /* Ugly exception: when the call was type(something),
                don't call tp_init on the result. */
            if (type == &PyType_Type &&
                PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 &&
                (kwds == NULL ||
                    (PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
                return obj;
            /* If the returned object is not an instance of type,
                it won't be initialized. */
            if (!PyType_IsSubtype(Py_TYPE(obj), type))
                return obj;
            type = Py_TYPE(obj);
            if (type->tp_init != NULL &&
                type->tp_init(obj, args, kwds) < 0) {
                Py_DECREF(obj);
                obj = NULL;
            }
        }
        return obj; 
    }
    

    在我们的例子中传给 type_call 的参数是什么呢?第一个是Joe自己---它是如何表示的呢?好吧,Joe是一个类(class),因此它是一个 类型(type) (Python3中所有类都是类型)。而类型在CPython虚拟机内部是通过 PyTypeObject 对象来表示的

    type_call 首先调用给定类型的 tp_new 属性。然后,检测一个特殊的情况(为简单起见可忽视先)以确保 tp_new 返回的是预期类型的对象,然后调用 tp_init 。如果返回的是一个不同类型的对象,则不将其初始化。

    从Python代码来看,就是发生了这些事情:如果你的类中定义了特殊方法 new ,当创建类的一个新实例时,首先调用这个特殊方法。这个方法必须返回某个对象。通常,返回的即是预期类型的对象,但是并非必须如此。所需类型的对象对其自身调用 init 。示例如下:

    class Joe:
        def __new__(cls, *args, **kwargs):
            obj = super(Joe, cls).__new__(cls)
            print ('__new__ called. got new obj id=0x%x' % id(obj))
            return obj
    
        def __init__(self, arg):
            print ('__init__ called (self=0x%x) with arg=%s' % (id(self), arg))
            self.arg=arg
    
    j = Joe(12)
    print(type(j))
    
    __new__ called. got new obj id=0x7f88e7218290
    __init__ called (self=0x7f88e7218290) with arg=12
    <class '__main__.Joe'>
    

    自定义过程

    如上所示,Joe的类型为 type ,所以调用函数 type_call 定义Joe实例的创建过程。可以通过为Joe指定一个自定义的类型来改变这个过程---换句话来说,这种自定义的类型就是一个metaclass。让我们修改前面的示例来为Joe指定一个自定义的metaclass:

    class MetaJoe(type):
        def __call__(cls, *args, **kwargs):
            print('MetaJoe.__call__')
            return None
    
    class Joe(metaclass=MetaJoe):
        def __new__(cls, *args, **kwargs):
            obj = super(Joe, cls).__new__(cls)
            print('__new__ called. got new obj id=0x%x' % id(obj))
            return obj
    
        def __init__(self, arg):
            print('__init__ called (self=0x%x) with arg=%s' % (id(self), arg))
            self.arg = arg
    
    j = Joe(12)
    print(type(j))
    

    现在Joe的类型不是 type ,而是 MetaJoe 。因此,当 PyObject_Call 为 j = Joe(12) 选择要执行的调用函数,它选择的是 MetaJoe.call 。后者先打印一条关于自己的提示,然后返回None,所以我们根本不要期望调用Joe的方法 newinit
    事实上,输出是这样的:

    MetaJoe.__call__
    <class 'NoneType'>
    
    1.从它们的函数定义 def new(cls, [...]) 和 def init(self, [...]) 可以知道, init 被调用时实例已经被创建(就是 self 参数所引用的);而 new 被调用的时候,实例不一定已被创建(暂时只能这么说),而 new 的第一个参数为当前类的引用。

    相关文章

      网友评论

          本文标题:python 对象创建过程

          本文链接:https://www.haomeiwen.com/subject/krdyattx.html