GIL(Global Interperter Lock)、
Python的线程是真正的Posix Thread,而不是模拟出来的线程
全局解释器锁,只在cpython上存在,是一个功能逻辑,并不是语言特性
cpython -> GIL
GIL是python解释器层面的锁,解决解释器中多个资源竞争线程的问题
python中的多线程不适合计算密集型程序
大部分的程序在运行时,都需要大量IO操作,比如网络数据的收发,大文件的读写,这样的程序称为IO密集型程序。
IO密集型程序在运行时,需要大量的时间进行等待,那么这时如果IO操作不完成,程序就无法执行后面的操作,导致CPU空闲。
IO操作是通过比如硬盘读写是不需要cpu的,所以可以使用多线程。
在Python解释器执行程序时,由于GIL的存在,导致同一时刻只能有一个线程执行,那么程序执行效率非常低,那么在程序进行IO读取时,CPU实际并没有做任何工作,为了提高CPU的使用率,那么Python解释在程序执行IO等待时,会释放 GIL 锁,让其它线程执行,提高Python程序的执行效率。
*线程释放情况:
时间片耗尽
线程遇到IO操作
任何python程序在执行前必须先获的GIL锁,然后每执行100条字节码,解释器就自动释放GIL,让别的线程有机会执行。(多条字节码表示一条代码)
*
解决办法
- python的高版本对GIL做了优化,但并不理想。
- 更换解释器,比如jpython,单支持的模块少
- 使用多进程+协程(python的多进程就是copy的多线程)
GIL是历史遗留问题,刚设计GIL的时候,cpu还都是单核的
现在有一款mac可以有6核心开12个线程
深拷贝和浅拷贝
在程序开发过程中,经常涉及到数据的传递,在数据传递使用过程中,可能会发生数据被修改的问题。为了防止数据被修改,就需要在传递一个副本,即使副本被修改,也不会影响原数据的使用。为了生成这个副本,就产生了拷贝。
引用:在 Python 程序中,每个对象都会在内存中申请开辟一块空间来保存该对象,该对象在内存中所在位置的地址被称为引用
函数参数传递-》引用
可变对象
a = 1
b = a
print(a, b)
print(id(a), id(b))
a = 2
print(a, b)
print(id(a), id(b))
不可变对象
l1 = [1, 2, 3]
l2 = l1
l1[0] = 11
浅拷贝:
不可变对象拷贝相当与赋值(没有必要)
浅拷贝的几种实现方法
copy.copy(obj)
obj.copy() # 大多数对象都有copy()方法
list(l1) # 工厂方法
[:] # 切片
可变对象的浅拷贝
例1
import copy
l1 = [1, 2, 3]
l2 = copy.copy(l1)
l1[0] = 11
例2: 复杂可变对象的浅拷贝
a = [1, 2]
l1 = [3, 4, a]
l2 = copy.copy(l1)
a[0] = 11
深拷贝
相对于浅拷贝只拷贝顶层的引用外,copy模块还提供了另外一个拷贝方法 deepcopy() 函数,这个函数可以逐层进行拷贝引用,直到所有的引用都是不可变引用为止。
import copy
a = [1, 2]
l1 = [3, 4, a]
l2 = copy.deepcopy(l1)
a[0] = 11
深拷贝可以解决数据在传递时不被修改的问题
浅拷贝在拷贝时,只拷贝第一层中的引用,如果元素是可变对象,并且被修改,那么拷贝的对象也会发生变化
深拷贝在拷贝时,会逐层进行拷贝,直到所有的引用都是不可变对象为止。
模块导入
如果有多个模块可以将这些模块放在一个文件中,并创建一个 init.py 的文件,这个文件夹称为 package。
模块的搜索顺序是
当前程序所在目录
当前程序根目录(project directory)
PYTHONPATH
标准库目录
第三方库目录site-packages目录
sys模块中的path变量记录了导入模块的查找位置:
import sys
sys.path
如果导入的模块不在 path 保存的路径中,那么导入模块时就会报错
ModuleNotFoundError: No module named ‘CC’
可以在程序中向 path 变量中添加模块所在的路径。
假定在路径 /Users/KG/Desktop 下有一个 CC.py 模块
在程序中将 /Users/KG/Desktop 路径加到 path中去
# 还可以是用insert方法
import sys
sys.path.append('/Users/KG/Desktop') # 添加自己的模块
2.1重新加载模块
模块导入成功后,在使用模块过程中,如果被导入的模块对数据进行了修改,那么正在使用该模块的程序并不会修改。
就算重新导入修改后的模块也不行
因为 程序在导入模块时,会将模块创建一个对象保存到一个字典中,如果之前导入过一次,再次导入时就不会再创建这个对象。(后面有图示)
可以通过 sys 模块下的 modules 属性来查看一个文件中导入过的模块。
判断 sys.moudle # 系统自带的模块(字典类型)-》添加数据
imp.reload() # 之后好像会被废弃
from imp import reload
reload(A)
A.n
importlib.reload()
from importlib import reload
reload(A)
A.n
2.2 import compare from … import …
import
import 方式导入时,只是在当前文件中建立了一个模块的引用,通过模块的引用来使用模块内的数据 。
使用这种方式导入时,访问控制权限对文件内级别的数据不起作用,通过模块名都可以进行访问。
相当于将一个模块中所有的内容都导入到当前文件中使用。
AA.py
x = 1
_y = 2
__z = 3
BB.py
import AA
print(AA.x)
print(AA._y)
print(AA.__z) # 虽然不提示,但是依然可以用
from … import …
from-import 方式在导入数据时,会将导入模块中数据复制一份到当前文件中,所以可以直接使用模块中的变量,函数,类等内容。
在使用 from-import 方式导入时,文件内私有属性 _xxx 形式的数据不会被导入。
在使用 from-import 方式导入时,如果模块内和当前文件中有标识符命名重名,会引用命名冲突,当前文件中的内容会覆盖模块的数据
all 魔法变量 在 Python 中还提供种方式来隐藏或公开数据 ,就是使用 all
all 本质是一个列表,在列表中以字符串形式加入要公开的数据
在使用 from-import 导入模块时,如果模块中存在这个变量,那么就按这个变量里的内容进行导入,没有包含的不导入。
#AA.py
__all__ = ['_y','__z']
x = 1 # 共有变量
_y = 2 # 私有变量
__z = 3 # 类私有属性
#BB.py
from AA import *
print(_y) #虽然是私有的,但是在 __all__中公开了就可以导入
print(__z)
# print(x) # 没有公开,不能使用
# show()
从使用便利的角度,使用from-import
从命名冲突的角度,使用 import
from import # copy one to localhost
不能导入私有变量 使用 all = [‘_y’,’__z’] ,解决命名冲突
循环导入问题
在开发过程中,可能会遇到这种情况。两个模块相互之间进行导入。这样的话,会造成程序出现死循环。程序运行时就会报错。(循环调用/导入)
#AA.py
from BB import *
def ashow():
print('A - show')
bshow()
#BB.py
from AA import *
def bshow():
print('A - show')
ashow()
>>>NameError: name 'ashow' is not defined
这是因为模块在导入时要经过这么几步:
- 在sys.modules 中去搜索导入的模块对象
- 如果没有找到就创建一个空模块并加入到sys.modules中,如果找到就不在创建
- 然后读取模块中的数据对空模块初始化
- 对存在的模块直接建立引用在当前文件中使用
下面的代码尝试去解决循环引用问题:
#AA.py
def ashow():
print('A - show')
import BB
BB.bshow()
#BB.py
def bshow():
print('B - show')
import AA
AA.ashow()
>>>程序输出结果:
A - show
B - show
A - show
dict
一個事實是:類別(Class)或實例(Instance)本身的作用是作為特性(Property)的名稱空間(Namespace)。類別或實例本身會擁有一個dict特性參考至一個字典物件,其中記錄著類別或實例所擁有的特性。
print(classname.dict) # 类名访问
print(obj.dict) # 对象访问
_classname__obj # 私有属性
>>> class Math:
... PI = 3.14159
...
>>> Math.PI
3.14159
>>> print(Math.__dict__)
{'__dict__': <attribute '__dict__' of 'Math' objects>, '__module__': '__main__',
'PI': 3.14159, '__weakref__': <attribute '__weakref__' of 'Math' objects>, '__d
oc__': None}
>>> Math.__dict__['PI']
3.14159
>>>
class Some:
def __init__(self, x):
self.x = x
s = Some(1)
Some.service = lambda self, y: print('do service...', self.x + y)
s.service(2) # do service... 3
如果你要刪除物件上的某個特性,則可以使用del。例如:
>>> class Some:
... pass
...
>>> s = Some()
>>> s.x = 10
>>> print(s.__dict__)
{'x': 10}
>>> del s.x
>>> print(s.__dict__)
{}
>>>
可以使用moudles.py 专门用来导入模块