跟着视频敲代码,却出错,真是奇了个怪了,不得不探究一下了。
出错代码:
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__。脚本如下:
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()
其实我也比较奇怪,这个脚本写的比较奇怪,class B的的__new__返回了A的实例。也只是只执行了B的__new__方法,并没有执行A的__init__方法。
深入 迷失
遇到这个问题:
要深入首先查看了一下代码的编译后的python指令,查看B,是B的__init__方法的指令,
如上图,为了具体查看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娇气啊……