疑惑 提出问题

前天同事问我一个问题,为什么这个脚本中的没有调用A 的__init__。脚本如下:

 class A(object):
def __init__(self, *args, **kwargs):
print "Call init from %s" %self.__class__ def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
print "Call new from %s" %obj.__class__
return obj class B(object):
def __init__(self, *args, **kwargs):
print "Call init from %s" %self.__class__ def __new__(cls, *args, **kwargs):
obj = object.__new__(A, *args, **kwargs)
print "Call new from %s" %obj.__class__
return obj b = B()

其实我也比较奇怪,这个脚本写的比较奇怪,class B的的__new__返回了A的实例。也只是只执行了B的__new__方法,并没有执行A的__init__方法。

深入 迷失

遇到这个问题:

要深入首先查看了一下代码的编译后的python指令,查看B,是B的__init__方法的指令,

Python没有执行__init__-LMLPHP

如上图,为了具体查看B()方法是如何调用,就添加了一个方法test_B。B()显示仅仅是两条指令

LOAD_GLOBAL

CALL_FUNCTION

查看Python源代码,到

PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)
{
    ternaryfunc call;

if ((call = func->ob_type->tp_call) != NULL) {
        PyObject *result;
        if (Py_EnterRecursiveCall(" while calling a Python object"))
            return NULL;
        result = (*call)(func, arg, kw);

**************看到这里的时候有点迷失了,不知道tp_call是个什么东西了,不确定由typeobject来操作

(这个必须进行检讨因为tp_call已已经明确了他来自哪里)***************

调试 问题解决

最后使出了,大招对Python源代码进行调试,在Linux上编译python源代码加上参数--with-debug, 然后执行gdb -ex r --args python test.py

在call_function,do_call,PyObject_Call 执行到之后,打上断点。看到他运行到了

type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    obj = type->tp_new(type, args, kwds);
    if (obj != NULL) {
        if (!PyType_IsSubtype(obj->ob_type, type))
            return obj;

,这个时候我再去看代码。发现哪里已经写好注释了:

/* If the returned object is not an instance of type,
           it won't be initialized. */

好吧,很明确了了。没有产生和class类型一致的instance,就不会进行初始化。即调用__init__。

问题解决了。。也学习了

要问我怎么知道在那个地方打断点,因为我已经看过了代码,只是理解没有那么深。

04-14 23:27