Python学习笔记二十一(GIL/深拷贝/浅拷贝/多态)
Python 多线程对CPU的使用率
单线程对CPU的一个核心使用率可以达到100%
# 多线程
import threading
# 子线程死循环
def test():
while True:
pass
# t1 = threading.Thread(target=test)
# t1.start()
# 主线程死循环
while True:
pass
01单线程对CPU的使用情况.png
那么要让CPU的两个核心使用率都达到100%,应该怎么做?使用两个线程?
两个线程对CPU的两个核心使用情况
# 多线程
import threading
# 子线程死循环
def test():
while True:
pass
t1 = threading.Thread(target=test)
t1.start()
# 主线程死循环
while True:
pass
02多线程对CPU的使用情况.png
两条线程对双核CPU的使用率大概是50%左右,远远达不到100%.这是为什么?说到这就绕不开一个概念GIL.
GIL(全局解释器锁)[1]
什么是GIL
GIL 的全程为 Global Interpreter Lock ,意即全局解释器锁,用来保护所有全局的解释器和环境状态变量的,线程执行必须拿到这把锁之后才能执行.
04单核多线程.pngGIL的来历, 单核怎么实现多任务? 为此GIL产生. 正是这个锁能保证同一时刻只有一个线程在运行, 也正是这个锁让单核也可以实现多任务.
那么怎么解决效率问题?
1.使用多进程+协程,完成任务
2.GIL 是CPython解释器的,换一个解释器JPython也可以解决
3.JPyhton解释器可以解决,调用Java写的多线程一样可以解决[2]
拷贝
什么是拷贝?
复制就是拷贝, 复制XXX文件, 粘贴之后得到 XXX副本, XXX副本 就是XXX 拷贝得到的. pyhon 中的拷贝分为浅拷贝 和深拷贝, 他们之间有什么区别呢?
引用和拷贝的区别
在讨论深浅拷贝的区别之前, 先讨论另一个问题, 引用 和 拷贝的区别.
# 引入copy 模块
import copy
a = [1, 2, 3]
# b引用 a
b = a
# c浅拷贝 a
c = copy.copy(a)
# d深拷贝 a
d = copy.deepcopy(a)
# 从值分析区别
print("从值分析区别")
print(a)
print(b)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(b))
print(id(c))
print(id(d))
# 运行结果
# 从值分析区别
# [1, 2, 3]
# [1, 2, 3]
# [1, 2, 3]
# [1, 2, 3]
# --------------------
# 从地址分析区别
# 140060678261192
# 140060678261192
# 140060702258888
# 140060678259976
从值以及地址两方面入手分析引用 与拷贝的区别.
- 值
从值上看不出引用 与拷贝的区别 - 地址
从地址上可以看出 b地址 = a地址, d/c 地址 != a地址 ,既 引用地址相等, 拷贝地址不相等
上面用的可变类型得出的结论,对于不可变类型是否依然成立?
# 引入copy 模块
import copy
a = 1
# b引用 a
b = a
# c浅拷贝 a
c = copy.copy(a)
d = copy.deepcopy(a)
# 从值分析区别
print("从值分析区别")
print(a)
print(b)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(b))
print(id(c))
print(id(d))
# 运行结果
# 从值分析区别
# 1
# 1
# 1
# 1
# --------------------
# 从地址分析区别
# 10919424
# 10919424
# 10919424
# 10919424
可以看出 对于不可变类型以上结论不成立.
深拷贝和浅拷贝的区别
引用和拷贝的区别分析完了, 那么深拷贝和 浅拷贝的区别是什么呢?
# 引入copy 模块
import copy
a = [[11, 22, 33], [33, 44, 55]]
# c浅拷贝 a
c = copy.copy(a)
d = copy.deepcopy(a)
# 从值分析区别
print("从值分析区别")
print(a)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(c))
print(id(d))
print("* *" * 10)
# 修改11 为100
a[0][0] = 100
# 从值分析区别
print("从值分析区别")
print(a)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(c))
print(id(d))
# 运行结果
# 从值分析区别
# [[11, 22, 33], [33, 44, 55]]
# [[11, 22, 33], [33, 44, 55]]
# [[11, 22, 33], [33, 44, 55]]
# --------------------
# 从地址分析区别
# 139975649195784
# 139975673194376
# 139975649196936
# * ** ** ** ** ** ** ** ** ** *
# 从值分析区别
# [[100, 22, 33], [33, 44, 55]]
# [[100, 22, 33], [33, 44, 55]]
# [[11, 22, 33], [33, 44, 55]]
# --------------------
# 从地址分析区别
# 139975649195784
# 139975673194376
# 139975649196936
分析深浅拷贝也是从值 和 地址两方面入手, 另改变了一次a[0][0]的值, 修改后地址以及值的变化.
- 地址
修改前后地址值没有发生变化, 这是一定的, 可变类型修改值地址不改变 - 值
值修改之后, c值 = a值, d值 != a值, 既浅拷贝随着改变而改变, 深拷贝不会随着改变而改变.
如果a 存储的是不可变类型或者 a 本身就是不可变类型上述结论是否成立?
a存储不可变类型
# 引入copy 模块
import copy
a = [11,22,33]
# c浅拷贝 a
c = copy.copy(a)
d = copy.deepcopy(a)
# 从值分析区别
print("从值分析区别")
print(a)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(c))
print(id(d))
print("* *" * 10)
# 修改11 为100
a[0] = 100
# 从值分析区别
print("从值分析区别")
print(a)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(c))
print(id(d))
# 运行结果
# 从值分析区别
# [11, 22, 33]
# [11, 22, 33]
# [11, 22, 33]
# --------------------
# 从地址分析区别
# 140393288703176
# 140393312696072
# 140393288701192
# * ** ** ** ** ** ** ** ** ** *
# 从值分析区别
# [100, 22, 33]
# [11, 22, 33]
# [11, 22, 33]
# --------------------
# 从地址分析区别
# 140393288703176
# 140393312696072
# 140393288701192
从上面的结果可以看出, 当a 存储的是不可变类型, 修改之后 c值 != a值 ,d值 != a值, 既a 存储不可变类型,修改值之后 ,拷贝不会随着改变而改变.
a是不可变类型
# 引入copy 模块
import copy
a = 1
# c浅拷贝 a
c = copy.copy(a)
d = copy.deepcopy(a)
# 从值分析区别
print("从值分析区别")
print(a)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(c))
print(id(d))
print("* *" * 10)
# 修改11 为100
a = 100
# 从值分析区别
print("从值分析区别")
print(a)
print(c)
print(d)
print("--" * 10)
# 从地址分析区别
print("从地址分析区别")
print(id(a))
print(id(c))
print(id(d))
# 运行结果
# 从值分析区别
# [11, 22, 33]
# [11, 22, 33]
# [11, 22, 33]
# --------------------
# 从地址分析区别
# 140393288703176
# 140393312696072
# 140393288701192
# * ** ** ** ** ** ** ** ** ** *
# 从值分析区别
# [100, 22, 33]
# [11, 22, 33]
# [11, 22, 33]
# --------------------
# 从地址分析区别
# 140393288703176
# 140393312696072
# 140393288701192
从上面的结果可以看出, 当a 是不可变类型, 修改之后 c值 != a值 ,d值 != a值, 既a 存储不可变类型,修改值之后 ,拷贝不会随着改变而改变.
怎么理解上述变化?
深拷贝是完全克隆, 浅拷贝是整容. 也就是说 深拷贝是完全拷贝, 浅拷贝只是拷贝第一层
深拷贝
05深拷贝.png浅拷贝
06浅拷贝.png小结:
- 引用不会开辟新的空间, 节约内存, 无法保证数据的完整性
- 浅拷贝, 只拷贝一层, 保证了最外层数据的完整性,开辟新的空间, 消耗少量内存
- 深拷贝, 递归拷贝, 拷贝了所有层级, 保证了整个数据的完整性,开辟大量新空间, 消耗大量内存
- 浅拷贝和深拷贝在拷贝不可变类型时,都不会开辟新的空间
- python 中提供的拷贝方法(list.copy, dict.copy,切片)都是浅拷贝.
私有化
- xx: 公有变量
- _x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
- __xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)
- xx:双前后下划线,用户名字空间的魔法对象或属性。例如:init , __ 不要自己发明这样的名字
- xx_:单后置下划线,用于避免与Python关键词的冲突
多态
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
def test(object):
object.run()
class Demo(object):
def run(self):
print('我来打酱油')
dog = Dog()
cat = Cat()
demo = Demo()
test(dog)
test(cat)
test(demo)
# 运行结果
# Animal is running...
# Animal is running...
# 我来打酱油
因为多态的条件是
- 必须承继父类
- 子类重写父类的方式
- 重写的方法被调用
结合程序运行结果以及多态的条件得出 Python中的多态是一个不严谨的多态.
到此结 DragonFangQy 2018.5.20