跟着视频敲代码,却出错,真是奇了个怪了,不得不探究一下了。

出错代码:

2023-10-11 16:20:18 [scrapy.core.scraper] ERROR: Error processing {'price': '13.58-16.78万', 'sales': '24272辆', 'title': '元PLUS'}
Traceback (most recent call last):
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\twisted\internet\defer.py", line 892, in _runCallbacks
    current.result = callback(  # type: ignore[misc]
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\scrapy\utils\defer.py", line 340, in f
    return deferred_from_coro(coro_f(*coro_args, **coro_kwargs))
  File "D:\Python\Project\爬虫基础\scrapy_01\scrapy_01\pipelines.py", line 15, in process_item
    print(self.res.inserted_id)
AttributeError: 'NoneType' object has no attribute 'inserted_id'

把代码来回挪位置,发现是初始函数def __init__(self)压根就不执行!


Python没有执行__init__

疑惑 提出问题

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

python执行是如何不结束 python执行完不退出_python执行是如何不结束

1 class A(object):
 2     def __init__(self, *args, **kwargs):
 3         print "Call init from %s" %self.__class__
 4 
 5     def __new__(cls, *args, **kwargs):
 6         obj = object.__new__(cls, *args, **kwargs)
 7         print "Call new from %s" %obj.__class__
 8         return obj
 9 
10 
11 class B(object):
12     def __init__(self, *args, **kwargs):
13         print "Call init from %s" %self.__class__
14 
15     def __new__(cls, *args, **kwargs):
16         obj = object.__new__(A, *args, **kwargs)
17         print "Call new from %s" %obj.__class__
18         return obj
19 
20 b = B()

python执行是如何不结束 python执行完不退出_python_02

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

深入 迷失

遇到这个问题:

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

python执行是如何不结束 python执行完不退出_Python_03

如上图,为了具体查看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__。

问题解决了。。也学习了

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

没有产生一致的实例,就不初始化了?就不执行__init__(self) 函数了?这个也太……

后来想一想,不实用函数自带的self传值,那该怎么传值呢?哦,就不执行了,值也传不了了。所以,所有的参数都要必须从self里面进行传递吧,否则就进行初始化了,这个也太娇气了吧😄。

具体代码如下:

from itemadapter import ItemAdapter
import pymongo


class Scrapy01Pipeline(object):
    def __init__(self):
        self.res = None
        print("开始")
        self.client = pymongo.MongoClient("mongodb://localhost:27017")
        self.db = self.client["cheshi"]
        self.collection = self.db["cars"]

    def process_item(self, item, spider):
        self.res = self.collection.insert_one(dict(item))
        print(self.res.inserted_id)
        return item

    def __del__(self):
        print("结束")

函数process_item中的所有值,都要必须在init里面进行定义,而且必须是self开头,不然,init它就不进行初始化了,真tm娇气啊……

python执行是如何不结束 python执行完不退出_Python_04

python执行是如何不结束 python执行完不退出_初始化_05