python学习之python中的一些坑

一、GIL

1、定义:
GIL全局解释器锁,每个线程执行过程中必须先获得GIL,保证同一时刻只有一个线程在执行,
GIL与python语言没关系,是CPython解释器才有的,在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100

2、GIL缺陷
1、无法利用CPU多核资源
2、执行计算密集型程序时慢,但是io密集型程序还是可以很快的,因为当某线程遇到IO操作时会释放GIL
3、如何避免GIL
1、IO密集型程序可以选多线程,计算密集型应考虑多进程(此方法缺点比较浪费资源)
2、换解释器,比如Jpython就没有GIL,(此方法缺点但是有些C模块就用不了)
3、因为python是胶水语言,可以用其他语言先编译好(比如C)
3、GIL与互斥锁的区别
GIL是解释器层面的,互斥锁是python代码层面的,为了防止两个线程同时修改一个内存数据,造成数据混乱

二、深拷贝与浅拷贝

1、先了解 “=” 操作

a = 1
b = a

在C语言中,变量b和变量a会有两个地址空间,并且a地址里的值会copy到b的地址里,但在python中,b = a 于如下代码:

int a = 1;
int *b;
b = &a;

也就是说b是指向a的,b和a指向同一块地址空间

2、浅拷贝
浅拷贝是对于一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容

In [57]: a = [11,22]

In [58]: b = [33,44]

In [59]: c = [a, b]

In [60]: d = c

In [61]: e = copy.copy(c)

In [62]: id(c)
Out[62]: 139688461853896

In [63]: id(d)
Out[63]: 139688461853896

In [64]: id(e)
Out[64]: 139688443862280

In [65]: a.append(55)

In [66]: a
Out[66]: [11, 22, 55]

In [67]: c
Out[67]: [[11, 22, 55], [33, 44]]

In [68]: d
Out[68]: [[11, 22, 55], [33, 44]]

In [69]: e
Out[69]: [[11, 22, 55], [33, 44]]

In [70]:  c.append(77)

In [71]: c
Out[71]: [[11, 22, 55], [33, 44], 77]

In [72]: e
Out[72]: [[11, 22, 55], [33, 44]]

In [73]: 


上述代码说明:c 和 d 指向同一块地址空间,e是新创建了一块地址空间,然后copy c,但只拷贝顶层,相当于:

c = [指向a的地址(相当于C语言中的指针),指向b的地址]
e = [指向a的地址(相当于C语言中的指针),指向b的地址]

相当于a,c[0],e[0]指向同一地址空间,但是c和e指向不同的地址空间,所以当a的内容改变时c[0]和e[0]也会变,但当c.append(77),e不会变
3、深拷贝
深拷贝是对于一个对象所有层次的拷贝(递归

In [74]: a = [11,22]

In [75]: b = [33,44]

In [76]: c = [a,b]

In [77]: d = copy.deepcopy(c)

In [78]: a.append(55)

In [79]: c.append(66)

In [80]: id(c)
Out[80]: 139688461853064

In [81]: 

In [81]: id(d)
Out[81]: 139688461852744

In [82]: id(a)
Out[82]: 139688461853512

In [83]: id(c[0])
Out[83]: 139688461853512

In [84]: id(d[0])
Out[84]: 139688461852936

In [85]: c
Out[85]: [[11, 22, 55], [33, 44], 66]

In [86]: d
Out[86]: [[11, 22], [33, 44]]

上述代码说明:d是新创建了一块地址空间,然后copy c,拷贝了所有曾,相当于:

c = [指向a的地址(相当于C语言的传值),b的值]
d = [a的值(相当于C语言的传值),b的值]

相当于a和c[0]与d[0]指向不同地址空间,并且c和d指向不同的地址空间,所以当a的内容改变时c[0]会变但d[0]不会变,但当c.append(66),d不会变

注意
1、无论copy还是deepcopy都会创建新的地址空间
2、如果copy.copy()和copy.deepcopy()对一个全部都是不可变的数据类型进行拷贝(如元组,且元组里也都是不可变数据类型),那么浅拷贝和深拷贝结果一样,都将不进行拷贝,因为元组是不可变数据类型,copy.copy会自动判断,如果拷贝的对象是元组他将变成指向。
但是如果拷贝的是数据有可变数据类型,即使元组是顶层(如元组中包含列表),深拷贝会进行深拷贝,但是浅拷贝依旧是指向

In [88]: a = [11,22]

In [89]: b = a

In [90]: c =copy.copy(a)

In [91]: id (a)
Out[91]: 139688461852104

In [92]: id(b)
Out[92]: 139688461852104

In [93]: id(c)
Out[93]: 139688443923272

In [94]: 

In [94]: a = (11,22)

In [95]: b = a

In [96]: c = copy.copy(a)

In [97]: id(a)
Out[97]: 139688461774792

In [98]: id(b)
Out[98]: 139688461774792

In [99]: id(c)
Out[99]: 139688461774792


3、拷贝的其他方式
列表的切片,字典的copy,等号的赋值(不会创建新的地址空间),函数的形参都相当于浅拷贝

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值