GIL全局解释器锁
-
描述Python GIL的概念, 以及它对python多线程的影响?编写⼀个 多线程抓取⽹⻚的程序,并阐明多线程抓取程序是否可⽐单线程性 能有提升,并解释原因。
Python语⾔和GIL没有半⽑钱关系。仅仅是由于历史原因在 Cpython虚拟机(解释器),难以移除GIL。
GIL:全局解释器锁。每个线程在执⾏的过程都需要先获取 GIL,保证同⼀时刻只有⼀个线程可以执⾏代码。
线程释放GIL锁的情况: 在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执⾏完毕后,必须重新获取GIL Python 3.x使⽤计时器(执⾏时间达到阈值后,当前线程释放 GIL)或Python 2.x,tickets计数达到100
Python使⽤多进程是可以利⽤多核的CPU资源的。
多线程爬取⽐单线程性能有提升,因为遇到IO阻塞会⾃动释放 GIL锁
-
解决方法
换解释器
在一些部位换语言
浅拷贝、深拷贝
1.浅拷贝
- 浅拷贝是对于一个对象的顶层拷贝(拷贝了引用,并没有拷贝内容)
2.深拷贝
- 深拷贝是对于一个对象所有层次的拷贝(递归)
3.拷贝的其他方式
切片表达式可以赋值一个序列(浅拷贝)
字典的copy方法可以拷贝一个字典(浅拷贝)
4.注意点
- 拷贝对不可变类型和可变类型的copy不同
1.copy.copy对于可变类型,会进行浅拷贝
2.copy.copy对于不可变类型,不会拷贝,仅仅是指向
私有化
xx:公有变量
_x:但前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
__xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)
xx:双前后下划线,用户名字空间的魔法对象或属性。例如:init,__不要自己发明这样的名字
xx_:单后置下划线,用于避免与Python关键词的冲突
import导入模块
- import搜素路径
improt sys
print(sys.path)
- 从上面列出的目录里依次查找导入的模块文件
- "表示当前路径
- 列表中的路径的先后顺序代表了python解释器在搜索模块时的先后顺序
- 重新导入模块
- 模块被导入后, import module 不能重新导入模块,重新导入需要reload
from imp import reload
reload(模块名)
- 多模块开发时注意点
- 要修改某个引用模块内的属性值,必须使用 import
封装、继承、多态
- 在使用面向过程编程时,当需要对数据处理时,需要考虑用哪个模板中哪个函数来进行操作,但是当用面向对象编程时,因为已经将数据存储到了这个独立的空间中,这个独立的空间(即对象)中通过一个特殊的变量(class)能够获取到类(模板),而且这个类中的方法是有一定数量的,与此类无关的将不会出现在本类中,因此需要对数据处理时,可以很快速的定位到需要的方法是谁 这样更方便
- 全局变量是只能有1份的,多很多个函数需要多个备份时,往往需要利用其它的变量来进行储存;而通过封装 会将用来存储数据的这个变量 变为了对象中的一个“全局”变量,只要对象不一样那么这个变量就可以再有1份,所以这样更方便
- 代码划分更清晰
多继承以及MRO顺序
1.单独调用父类的方法
2.多继承中super调用有所父类的被重写的方法
* 如果使用super().xxxx()那么会拿着当前类的名字到类名.mro获取到的元组中去找,只要找到,那么就调用它的下一个方法
* 如果使用super(类名,self).xxxx()那么会拿着类的名字到类名.mro获取到的元组中去找,只要找到,那么就调用它的下一个方法
3.单继承中super
总结
1.super().init相对于类名.init,在单继承上用法基本无差
2.但在多继承上有区别, super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果
3.多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
4.单继承时,使用suoer方法, 则不能全部传递,只能传父类方法所需的参数,否则会报错
5.多继承时,相对与使用类名.init方法,要把每个父类全部写一遍,而使用super方法,只需写一句话便执行全部父类的方法, 这也是为何多继承需要全部传参的一个原因
静态方法和类方法
1.类属性、实例属性
- 实例属性属于对象:实例属性在每个对象中都要保存一份
- 类属性属于类:类属性内存中只保存一份
- 应用场景:通过类创建实例对象时,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份即可
2.实例方法,静态方法和类 方法;三种方法在内存中都归属于类,区别在于调用方式不同
实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self
类方法:由类调用;至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls
静态方法:由类调用;无默认参数
相同点:对于所有的方法而言,均属于类,所以在内存中也只保存一份
不同点:方法调用者不同、调用方法时自动传入参数不同
property属性
- 一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法
- 定义时,在实例方法的基础上添加@property装饰器;并且仅有一个self参数
- 调用时,无需括号
方法:foo_obj.func()
property属性:foo_obj.prop
- Python的property属性的功能是 :property属性内部进行一系列的逻辑计算,最终将计算结果返回
property属性的两种方式
- 装饰器即:在方法上应用装饰器
- 类属性即:在类中定义值为property对象的类属性
装饰器方式
经典类中的属性只有一种访问方式,其对应被@property修饰的方法
新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法,可以根据几个属性的访问特点分别将三个方法定义为对同一个属性:获取、修改、删除
当使用类属性的方式创建property属性时,经典类和新式类无区别
property方法的四个参数
- 第一个参数是方法名,调用对象.属性时自动触发执行方法
- 第二个参数是方法名,调用对象.属性 = xxx时自动触发执行方法
- 第三个参数是方法名,调用del对象.属性时自动触发执行方法
- 第四个参数是字符串,调用对象.属性.doc,此参数是该属性的描述信息
总结
- 定义property属性共有两种方式,分别是【装饰器】和【类属性】,而【装饰器】方式针对经典类和新式类又有所不同
- 通过使用property属性,能够简化调用者在获取数据的流程
proeprty属性应用
- 重新实现一个属性的设置和读取方法,可做边界判定
魔法属性
1.doc:表示类的描述信息
2.module和class
- module表示当前操作的对象在那个模块
- class表示当前操作的对象类是什么(当获取实例属性的时候,如果实例对象中没有这个属性,那么就会按照class指定的类对象中找到这个类属性)
3.init:初始化方法,通过类创建对象时,自动触发执行
4.del:当对象在内存中被释放,自动触发执行(对象计数为0或程序执行结束时触发)
5.call:对象后面加括号,触发执行
6.dict:类会对象中的所有属性
7.str:如果一个类中定义了str,那么在打印对象时,默认输出该方法的返回值(只要是获取这个对象的描述的情况,都会调用)
8.getitem、setitem、delitem
- 用于索引操作,如字典,以上分别表示获取、设置、删除
9.getslice、setslice、delslice
- 该三个方法用于分片操作,如:列表
面向对象设计
- 继承-是基于Python中的属性查找(如X.name)
- 多态-在X.medthod方法中,method的意义取决于X的类型
- 封装-方法和运算符实现行为,数据隐藏默认是一种惯例
with与"上下文管理器"
- 系统资源如文件、数据库连接、socket而言。应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源
- 上下文管理器:任何实现了enter()和exit()方法的对象都可称之为上下文管理器
- with语法用于简化资源操作的后续清除操作,是try/finally的替代方法,实现原理建立在上下文管理器之上