GIL, 深拷贝和浅拷贝, 模块导入, 重新导入模块,__dict__

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 专门用来导入模块

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值