一、GIL锁
-
1.1、GIL面试题:描述Python GIL的概念, 以及它对python多线程的影响?编写一个多线程抓取网页的程序,并阐明多线程抓取程序是否可比单线程性能有提升,并解释原因。
- Guido的声明: he language doesn't require the GIL -- it's only the CPython virtual machine that has historically been unable to shed it.
-
1.2、参考答案:
- (1)、Python语言和GIL没有半毛钱关系。仅仅是由于历史原因在Cpython虚拟机(解释器),难以移除GIL。
- (2)、GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码。
- (3)、线程释放GIL锁的情况: 在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100
- (4)、Python使用多进程是可以利用多核的CPU资源的。
- (5)、多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁
二、浅拷贝
2.1、浅拷贝是对于一个对象的顶层拷贝,简单的理解是:拷贝了引用,并没有拷贝内容。
-
2.2、 看一个最简单的浅拷贝
以上:a 与 b 的内存地址相同,说明了当给一个变量赋值的时候,其实就是将数据的引用赋值了一份给另外一个变量,这其实就是最简单的浅拷贝,不仅列表是这样,只要是 类似于 xx1 = xx2 的这种基本都是 浅拷贝,如下:>>> c = {"age":23} >>> d = c >>> id(c) 4527333240 >>> id(d) 4527333240 >>> c["name"] = "王小二" >>> c {'age': 23, 'name': '王小二'} >>> d {'age': 23, 'name': '王小二'}
因为都是浅拷贝,所以只要通过一个引用进行了修改,那么另外一个变量就看到的数 据也就变化了
-
2.3、
import copy
模块中copy.copy()
的使用>>> a = [11,22] >>> b = [33,44] >>> c = [a,b] >>> id(a) 4528760776 >>> id(b) 4528427464 >>> id(c) 4528012488 >>> import copy >>> d = copy.copy(c) >>> id(d) 4528788488 >>> id(d[0]) 4528760776 >>> id(d[1]) 4528427464 >>> a.append(55) >>> c [[11, 22, 55], [33, 44]] >>> d [[11, 22, 55], [33, 44]]
浅拷贝 只会复制最顶层的那个列表
三、深拷贝
-
3.1、深拷贝是对于一个对象所有层次的拷贝(递归)
>>> a = [11,22] >>> b = copy.deepcopy(a) >>> a [11, 22] >>> b [11, 22] >>> id(a) 4528761928 >>> id(b) 4528933000
以上结果通过
deepcopy()
确实将列表a
中所有的数据的引用copy
了,而不是只拷贝了a
指向的列表的引用,看如下,a的数据发生变化的时候,b并不会发生变化>>> a.append(55) >>> a [11, 22, 55] >>> b [11, 22]
-
3.2、进一步理解 深拷贝
深拷贝: 我个人理解其实是:深拷贝后与原来的对象没有任何关系了,不管原来的对象如何变化,都不再会影响到深拷贝后的对象
四、拷贝的其他方式
-
4.1、分片表达式 可以赋值一个序列
>>> a = [11,22] >>> b = [33,44] >>> c = [a,b] >>> d = c[:] >>> id(c) 4528760776 >>> id(d) 4528932936 >>> id(c[0]) 4528933000 >>> id(d[0]) 4528933000 >>> a [11, 22] >>> a.append(55) >>> c [[11, 22, 55], [33, 44]] >>> d [[11, 22, 55], [33, 44]]
d=c[:]
与d=copy.copy(c)
一样 属于 浅拷贝 -
4.2、字典的copy方法可以拷贝一个字典
五、注意点
-
5.1、浅拷贝对不可变类型和可变类型的copy不同
-
copy.copy 对于
可变类型
,会进行浅拷贝>>> a = [11,22] >>> b = copy.copy(a) >>> id(a) 4528932552 >>> id(b) 4528932680 >>> a.append(33) >>> a [11, 22, 33] >>> b [11, 22]
解释一下大家的对于
a
变化,而b
不变化的问题,因为b拷贝的是a里面11,22
引用的指向,当a添加 33的时候,而11,22
引用的指向并没有发生变化,所以并不会发生变化 -
copy.copy 对于
不可变类型
,不会拷贝,仅仅是指向>>> a = (11,22,33) >>> b = copy.copy(a) >>> id(a) 4528768632 >>> id(b) 4528768632
-
如果c是元组,那么copy时会,仅仅是元组的引用copy,而 deepcopy依然是深copy,即递归copy所有
>>> a = [11,22] >>> b = [33,44] >>> c = (a,b) >>> d = copy.copy(c) >>> id(c) 4528885256 >>> id(d) 4528885256 >>> a.append(55) >>> c ([11, 22, 55], [33, 44]) >>> d ([11, 22, 55], [33, 44]) >>> e = copy.deepcopy(c) >>> id(c) 4528885256 >>> id(e) 4528885448 >>> a.append(66) >>> c ([11, 22, 55, 66], [33, 44]) >>> e ([11, 22, 55], [33, 44]) >>>
-
-
5.2、copy.copy和copy.deepcopy的区别
-
copy.copy
>>> a = [11,22] >>> b = (a,) >>> c = [b,] >>> >>> d = copy.copy(c) >>> >>> c [([11, 22],)] >>> d [([11, 22],)] >>> a.append(33) >>> c [([11, 22, 33],)] >>> d [([11, 22, 33],)] >>> id(c) 4528932936 >>> id(d) 4528012488 >>> id(c[0]) 4528434200 >>> id(d[0]) 4528434200
d = c # 让d这个变量指向c指向的空间,d = copy.copy(c) # 复制所有c指向的数据到一个新的空间,但是不会递归copy
-
copy.deepcopy
>>> a = [11,22] >>> b = (a,) >>> c = [b] >>> >>> d = copy.deepcopy(c) >>> >>> c [([11, 22],)] >>> d [([11, 22],)] >>> >>> id(c) 4528932680 >>> id(d) 4528761928 >>> >>> id(c[0]) 4528913040 >>> id(d[0]) 4528913096 >>> >>> a.append(33) >>> >>> c [([11, 22, 33],)] >>> d [([11, 22],)] >>>
-