python的基础知识

1.面向对象的概念,特性?与其他语言对比

  • python是面向对象编程,简称OOP,是一种程序设计思想
    主要包括:类、继承、多态(子类方法覆盖父类方法)、实例、属性、方法
  • 面向对象和面向过程语言的区别:
    面向对象是把问题抽象成一个个对象,给这些对象绑上属性和方法,即以功能而非步骤划分问题。
    优点:更易低耦合、易维护、易复用;
    缺点:因为需要实例化所以开销较大,性能比面向过程要低。
    面向过程是把问题拆分成一个个函数和数据,按照一定的顺序来执行这些方法。
    优点:性能较高;
    缺点:不易维护,不易复用
  • Python是解释型语言:代码执行时才一行行动态翻译执行
    优点:无需翻译器,方便程序移植;可以交互式运行
    缺点:运行效率较低。
  • 编译型语言:在程序执行前,需要把源代码编译成机器语言的文件,再执行时可以直接使用该编译结果,以后不需要再次编译。
    优点:运行效率高
    缺点:难以移植
  • 类的定义和概念:
    类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
    类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
    数据成员: 类变量或者实例变量用于处理类及其实例对象的相关的数据。
    方法重写: 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
    实例变量:定义在方法中的变量,只作用于当前实例的类。
    继承: 一个新类可以继承一个设计好的类,即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,素以Dog也是一个Animal。子类可以继承父类所有的公有属性和公有方法
    实例化:创建一个类的实例,类的具体对象。
    方法: 类中定义的函数。
    对象: 通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
  • python的面向对象特性
    1.封装(隐藏): 隐藏对象的属性和实现细节,只对外提供必要的方法 。相当于将"细节封装起来",只对外暴露“相关调用方法”。通过私有属性、私有方法的方式实现封装。
    2.继承: 一个新类可以继承一个设计好的类,即子类可以继承父类所有的公有属性和公有方法。Python支持多重继承,一个子类可以继承多个父类。如果在类定义中没有指定父类,则默认父类是object类,也就是说object类是所有类的父类,因此所有类都有object类的属性和方法。
    类成员的继承和重写:
    1.成员继承:子类继承了父类除构造方法之外的所有成员。
    2.方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类中的方法,也称为重写。
    3.多态: 指同一个方法调用,由于对象不同可能会产生不同的行为。
    1.多态是方法的多态,属性没有多态
    2.多态的存在有两个必要条件:继承、方法重写
    (一个类它继承了一个父类,但是它又改写了它父类的方法,这样在调用这个方法时,就会因为实例对象的不同而调用的方法不同,也就是说看这个实例对象实例化时是用父类实例化的,还是子类实例化的,是父类实例化的,结果就是父类的方法,是子类实例化的,结果就是子类的方法)

C/C++、Java与Python的区别
1.c++、java和python都是面向对象的编程语言,但是c++和java都是强类型语言,而python是一种弱类型语言,C语言是面向过程的语言
2.垃圾回收机制:c++需要程序员收到回收,而java和python都有自己的垃圾回收机制GC。具体两者又有不同,python的垃圾收集机制主要是用的是引用计数方式。
3.c和java中变量的存储是真实值,而python总存储的是引用,所以python不用声明类型名称,它的输入均默认为字符串。
4.c++中用const来声明常量,java中使用final来声明,python中没有常量。
5.异常捕获处理结构:java和c++使用的是try catch结构,python使用的是try except结构。
6.C++为编译型语言;python为解释型的脚本语言。C++效率高,编程难;python效率低,编程简单。JAVA半编译半解释。
7. C和C++中都有结构的概念,但是在C语言中结构只有成员变量,而没成员方法,而在C++中结构中,它可以有自己的成员变量和成员函数。但是在C语言中结构的成员是公共的,什么想访问它的都可以访问;而在VC++中它没有加限定符的为私有的。

2.python的数据类型和数据结构

2.1 数据类型
不可变数据(3 个):Number(数字, int、float、bool、complex)、String(字符串)、Tuple(元组);
可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。相关知识点:
查看数据类型:type()
数据类型转换:str()、int()、list()、set()

字符串:
1、反斜杠可以用来转义,使用r可以让反斜杠不发生转义。
2、字符串可以用+运算符连接在一起,用*运算符重复。
3、Python中的字符串有两种索引方式,从左往右以0开始,从右往左以-1开始。
4、Python中的字符串不能改变。
List列表:
1、List可以使用+操作符进行拼接。
2、List中的元素是可以改变的,且元素类型可以不一样
Tuple(元组): 元组的元素不能修改,且类型可以不一样,写在小括号 ()
Set集合: 进行成员关系测试和删除重复元素,创建一个空集合必须用 set()
字典dict: 是一种映射类型,它的元素是键值对。
2、字典的关键字必须为不可变类型,且不能重复。
3、创建空字典使用 { }。
将整数转化成二进制字符串:
(1) >>>bin(12345).replace(‘0b’,’’)
‘11000000111001’
(2) >>> “{0:b}”.format(12345)
‘11000000111001’

2.2数据结构
Python中的内置数据结构(Built-in Data Structure):列表list、元组tuple、字典dict、集合set。
Python基本数据类型和数据结构_qq_45905052的博客-CSDN博客
list.append(x): 把一个元素添加到列表的结尾,相当于 a[len(a):] = [x]。
list.extend(L): 通过添加指定列表的所有元素来扩充列表,相当于 a[len(a):] = L。
list.insert(i, x): 在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如 a.insert(0, x) 会插入到整个列表之前,而 a.insert(len(a), x) 相当于 a.append(x) 。
list.remove(x): 删除列表中值为 x 的第一个元素。如果没有这样的元素,就会返回一个错误。
list.pop([i]): 从列表的指定位置移除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从列表中被移除。(方法中 i 两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在 Python 库参考手册中遇到这样的标记。)
list.clear(): 移除列表中的所有项,等于del a[:]。
list.index(x): 返回列表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。
list.count(x): 返回 x 在列表中出现的次数。
list.sort(): 对列表中的元素进行排序。
list.reverse(): 倒排列表中的元素。
**list.copy() :**返回列表的浅复制,等于a[:]。

3.装饰器

装饰器可以在不改变函数代码和调用方式的情况下给函数添加新的功能。

本质上是一个嵌套函数,接收被装饰的函数(func)作为参数,并返回一个包装过的函数,以实现不影响函数的情况下添加新的功能。抽离出大量与函数主体功能无关的代码,增加一个函数的重用性。

应用场景:性能测试(统计程序运行时间)、插入日志、权限校验

4.python的内存管理机制

python的内存管理机制=垃圾回收机制+内存池机制
1 垃圾回收机制

以引用计数法为主,标记-清除为辅。
(1)引用计数法:
指在每个对象的结构体PyObject中定义一个计数变量ob_refcnt,计算该对象被引用的次数,
引用计数变量增加的情况:1.该对象被引用;2.该对象被加入某容器(列表、字典等);3.对象被创建;4.该对象被作为参数传到函数中。
引用计数减少的情况:
1.使用del语句对对象别名显式的销毁(例如:del b)
2.对象所在的容器被销毁或从容器中删除对象(例如:del c )
3.引用超出作用域或被重新赋值(例如:a=[3,4])

优点:高效,自动释放,实时性高,即一旦没有引用立即释放;
缺点:1.增加存储空间,维护引用计数消耗资源;2.无法解决循环调用(一组未被外部使用、但相互指向的对象)的情况。

(2)标记-清除
用来解决引用计数机制产生的循环引用,进而导致内存泄漏的问题 。 循环引用只有在容器对象才会产生,比如字典,元组,列表等。辅助python进行垃圾回收。
过程:①首先创建对象时,python会将其加入零代链表;②遍历零代链表,如果被本链表内的对象引用,自身的被引用数-1;③清除被引用数为0的对象;④其余未被回收的“活动对象”升级为一代链表 一代同样可能会触发gc机制,从而对一代链表标记清除,将剩下的活动对象升级为二代链表。
两个阶段:
标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达
清除阶段,再次遍历对象,如果发现某个对象没有标记为可达(即为Unreachable),则就将其回收

2 内存池机制
python引用了一个内存池memory pool机制,即pymalloc机制,用于对小块内存的申请和释放。

当创建大量消耗小内存的对象时,频繁调用malloc/new会导致大量的内存碎片,导致效率降低。内存池即预先在内存中申请一定数量的、大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给该需求,不够了再去申请新的内存。对象被释放后,空闲的小内存返回内存池,避免频繁的内存释放动作。

优点:能够减少内存碎片,提升效率
当申请内存小于256k的话,就会直接在内存池中申请内存空间。如果申请内存大于256k的话则会直接执行new/malloc行为来申请新的内存空间。

5.生成器和迭代器

生成器:生成器是列表生成式的简化,列表生成式一次性生成整个列表,如果列表尺寸过大而我们只需要列表的前几个数据进行处理的话就会造成大量的空间浪费,所以引入生成器。
迭代器:迭代器的定义是可以用被next()函数调用并不断返回下一个值的对象。
区别:
①生成器是生成元素的,迭代器是访问集合元素的一种方式;
②迭代输出生成器的内容;
③迭代器是一种支持next()操作的对象;
④迭代器(iterator):其中iterator对象表示的是一个数据流,可以把它看做一个有序序列,但我们不能提前知道序列的长度,只有通过next()函数实现需要计算的下一个数据。可以看做生成器的一个子集。

  • 生成器自动实现了迭代器协议,而且将输出状态挂起,有延时输出效果。
  • 能够利用for进行遍历,是因为list本身就是一个可迭代对象,内部实质上已经实现了__iter__方法。
  • 要想成为迭代器,还需要实现迭代器协议:即实现next方法,要么返回下一个元素,要么引起终止迭代。

6.常用函数

6.1 lambda函数
匿名函数形式 f=lambda x : x表达式
调用时可采用f(x)的形式 或者直接使用(可以用于return语句)
优点:没有名字,无需担心函数冲突
缺点:只能有一个表达式,没有return,返回值就是该表达式的结果
6.2 append函数、extend()函数

  • append()添加的时候会把添加的数据当成一个整体进行添加,允许添加任意类型的数据
  • extend()添加的时候会把添加的数据迭代进行添加(添加元素),只允许添加可迭代对象数据(可迭代对象:能用for循环进行迭代的对象就是可迭代对象, 比如:字符串,列表,元祖,字典,集合等等)

6.3 range函数
range()是生成一个list,是一个列表生成式,而xrange()是一个生成器,每次调用返回其中的一个值。xrange做循环的性能比range好,尤其是返回很大的时候。尽量用xrange,除非是要返回一个列表。
6.4 字符串的拆分方法:

  • string对象的split方法,不允许有多个分隔符
  • 函数re.split(),允许为分隔符指定多个正则表达式
  • str.split(字符串切片)\str.find(查找字符)\str.strip(去除首尾指定字符)

6.5 深浅拷贝

Python的深浅拷贝讲解!
在这里插入图片描述

(1)所有的Python对象都拥有3个属性:身份(id,内存中的地址),类型(type),值(value).

对象:任何数据类型都是一个对象,不管是数字、字符串、字典等常见的数据结构,还是函数,甚至是我们导入的模块等,Python都会把它当做是一个对象来处理。

不可变数据类型:数值、字符串、布尔值。不可变对象就是对象的身份和值都不可变。新创建的对象被关联到原来的变量名,旧对象被丢弃,垃圾回收器会在适当的时机回收这些对象。(当我们对字符串、数值型、布尔值的数据改变变量名,并不会影响到数据在内存中的位置

可变数据类型:列表、字典、集合。所谓的可变指的是可变对象的值可变,但是身份是不可变的。(虽然是相同的数据,但是变量名字不同,内存中仍然会开辟新的内存地址来进行存放相同的数据

(2)引用:在Python语言中,每个对象都会在内存中申请开辟一块新的空间来保存对象;对象在内存中所在位置的地址称之为引用。(是自动形成的从变量到对象的指针),定义的变量名实际上就是对象的地址引用。

赋值:赋值其实就是将一个对象的地址赋值给一个变量,使得变量指向该内存地址。

  • 相同数据,不同变量名:每次赋值都会开辟新的内存地址来存放数据,比如我们同时在v1,v2存放一个列表[1,2,3],即使数据是相同的,但是v1,v2的内存地址却不同。
  • 一个变量多次赋值:对一个变量v1多次赋值,其内存是会变化的
  • 将一个变量赋值给另一个变量,其实它们就是同一个对象:数据相同,在内存中的地址也相同
  • 嵌套赋值:如果是列表中嵌套着另外的列表,那么当改变其中一个列表的时候,另一个列表中的也会随着改变
import copy
lst1 = copy.copy(lst) #浅拷贝
lst1 = copy.deepcopy(lst) #深拷贝

(3)浅拷贝:只是拷贝数据的第一层,不会拷贝子对象。
不可变类型的浅拷贝: 如果只是针对不可变的数据类型(字符串、数值型、布尔值),浅拷贝的对象和原数据对象是相同的内存地址。只是换了一个名字,对象在内存中的地址其实是不变的。
可变类型的浅拷贝:

  • 列表本身的浅拷贝对象的地址和原对象的地址是不同的,因为列表是可变数据类型。
  • 列表中的元素(第1个元素为例)和浅拷贝对象中的第一个元素的地址是相同的,因为元素本身是数值型,是不可变的。

在可变类型的数据中,如果存在嵌套的结构类型,浅拷贝只复制最外层的数据,导致内存地址发生变化,里面数据的内存地址不会变。

三种形式:切片操作,工厂函数,copy模块中的copy函数。
如: lst = [1,2,[3,4]]
切片操作:lst1 = lst[:] 或者 lst1 = [each for each in lst]
工厂函数:lst1 = list(lst)
copy函数:lst1 = copy.copy(lst)

  • 浅拷贝中存放的是对可变对象的引用,如果把a拷贝成b,a中的可变对象发生变化的话,b中的对应对象也随之发生变化,即ab中的子类可变对象其实是共享的,虽然拷贝对象与原对象的内存地址不同,但是两者中的元素具有相同的内存地址。

(4)深拷贝: 深拷贝会拷贝所有的可变数据类型,包含嵌套的数据中的可变数据。深拷贝是变量对应的值复制到新的内存地址中,而不是复制数据对应的内存地址。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。

不可变类型的深浅拷贝,其效果是相同的

可变类型的深拷贝:

  • 深拷贝对最外层数据是只拷贝数据,会开辟新的内存地址来存放数据。
  • 深拷贝对里面的不可变数据类型直接复制数据和地址,和可变类型的浅拷贝是相同的效果。

用copy模块中的deepcopy实现,是把所有可变对象、不可变对象的内容都通过递归的方式一一拷贝了过来,ab是不相同的内存地址,ab中的元素内存地址也不相同,两者完全独立。a中的可变对象发生改变的话不会影响b中对象的改变

通过大量的例子,我们得出结论:

  • 在不可变数据类型中,深浅拷贝都不会开辟新的内存空间,用的都是同一个内存地址。
  • 在存在嵌套可变类型的数据时,深浅拷贝都会开辟新的一块内存空间;同时,不可变类型的值还是指向原来的值的地址。
  • 不同的是:在嵌套可变类型中,浅拷贝只会拷贝最外层的数据,而深拷贝会拷贝所有层级的可变类型数据。

6.6.is和==的区别

变量:用来标识(identify)一块内存区域。为了方便表示内存,我们操作变量实质上是在操作变量指向的那块内存单元。编译器负责分配。我们可以使用Python内建函数id()来获取变量的地址

变量名:是一个标识符(dientify),用来代之一块内存空间,使用这个变量名,我们可以很方便的操作这块内存区域。

内存:内存是我们电脑硬件,用来存放数据,形象的理解就是内存有一个一个的小格子组成,每个格子的大小是一个字节,每个格子可以存放一个字节大小的数据。我们如何才能知道,数据存放在哪些格子中,那就得靠地址,地址类似于楼房的门牌号,是内存的标识符。

  • is判断两个对象的内存地址是否相同
  • ==判断两个对象的值是否相同
    a is b 相当于 id(a)==id(b),id() 能够获取对象的内存地址。

Python出于对性能的考虑,但凡是不可变对象,在同一个代码块中的对象,只要是值相同的对象,就不会重复创建,而是直接引用已经存在的对象。

### 6.7 python传参时需要注意什么
(1) 传入参数是否可以被修改
1.如果没有将外部变量传递到函数来(参数定义种包含该外部变量),则函数内部虽然可以使用,但是不能对该变量进行修改(会报错)——未传参的变量可使用但不可修改
2.将外部变量作为实参传递到函数中来的话,如果外部变量是不可变对象的话不可以修改该变量的原始值——相当于“传值”的方式来传递变量,可以通过改变引用指向的方式,在函数内部获取新的不可变对象,但是对函数外部没有影响(除非加了global)

python函数传入的是原变量引用的一个副本,这两个引用(副本及原引用)在内外均指向同一个内存区(具体存值的地方)。如果在函数内部改变了引用指向的话,只是副本引用指向了一个新的内存区,函数外面的引用仍然指向原内存堆区,不会改变原变量的值(不可变对象的操作、可变对象的赋值操作)。而如果在内部通过append、remove函数改变了堆区的值的话,外部的引用仍然会改变。

(2) 传入参数的顺序和类型
参数顺序:必选参数,默认参数,可变参数,命名关键字参数,关键字参数

可变参数必须在关键字参数之前
默认参数 必须指向不可变对象
可变参数 形式def func(可变参数) 传入参数的个数可以是0个或者多个,其实是传入一个list或者tuple,函数接收的是个tuple。如果有个list或者tuple想直接传入func的话,可以直接用func(list)实现(即把想要传入的可变参数封装成list/tuple传入)
关键字参数 形式def func(**关键字参数) 允许传入0个或多个含参数名的参数,在函数内部组装成字典。传入的时候用参数名=参数值的形式。如果传入dict的话可以直接func(**dict)
命名关键字参数 形式def func(
,city,job) 限制某几种参数名的输入,传入的时候必须只有
,后面的这几种参数名可以选择,传入用fun(city=xxx,job=xxx)的形式

6.8python的list和numpy.array(数组)的区别

1.list可以存放不同类型的数据,比如int,str,bool,float,list,set等,但是numpy数组中存放的数据类型必须全部相同,必须全为int或全为float

2.list中每个元素的大小可以相同,也可以不同,因此不支持一次性读取一列,而numpy数组中每个元素大小相同,支持一次性读取一列。numpy对二维数组的操作更为便捷。

3.list中数据类型保存的是数据存放的地址,即指针而非数据,比如一个有四个数据的列表a=[1,2,3,4]需要4个指针和4个数据,增加了存储空间,消耗CPU,但是a=np.array([1,2,3,4])只需要存放4个数据,节省内存,方便读取和计算

4.一个numpy的array是内存中的一个连续块,array中的元素都是同一类型的,所以一旦确定了一个array,它的内存就确定了,每个元素的内存大小都确定了。而list的每个元素是一个地址的引用,这个地址又指向了另一个元素,这些元素在内存里不一定是连续的,因此每当加入新元素,其实只是把这个元素的地址添加到list中。

9)类中self的概念
self代表实例本身,具体来说是该实例的内存地址

self的三种应用:
1.self为类中函数的第一个参数
在调用实例方法时,python解释器会自己把实例变量传递给函数中的self。如果类的实例方法的第一个参数不是代表实例的self,则调用实例方法时,该方法没有参数来接收自动传入的实例变量,程序会产生异常。

2.在类中,引用实例的属性 self.变量名

3.引用实例的属性目的是为实例绑定属性,写入或读取实例的属性

在类中,调用实例的方法 self.方法名()

6.10 tuple元祖和列表list,字典dict,集合set什么区别?**

 tuple:元组,元素不可改(元组内的元素不可改,整个元组可修改)
 list:列表,元素可修改 可以使用+操作符进行拼接。
都按顺序保存元素,每个元素都有自己的索引,因此列表和元组都可通过索引访问元素。
 dict:字典,获取元素的方式不同。列表通过索引值获取,字典通过键获取;数据结构和算法不同。字典是 hash 算法,搜索的速度特别快;占用的内存不同。
 set:集合,无序不重复元素集合。如果将字典转化为集合,仅仅只是将键(key)收录到集合中。如果想将字典中的值转化为集合,可以用字典函数values()。
字典(dict)和集合(set)类似,它们存储的数据都是无序的,其中字典是用 key-value 的形式保存数据。
11) python文件操作

f = open("file_test","r",encoding = "utf-8") #r只读,w写,r+读写,
f1.close()
f3.readline() #按行读,读取文件一行的内容
f.readlines()  #读取所有的行到数组里面[line1,line2,...lineN]
f.write(string) #:把string字符串写入文件。
f.writelines(list) #:把list中的字符串一行一行地写入文件,是连续写入文件,没有换行。

6.12 sort和sorted函数的区别

sorted(): 该函数第一个参数iterable为任意可以迭代的对象,cmp是比较函数,通常为lambda函数,key是用于比较的关键字,reverse表示排序结果是否反转。该函数会返回一个排序后的列表,原有可迭代对象保持不变,这与L.sort()不同。

L = ['cat', 'binary', 'big', 'dog']
print sorted(L, key=lambda x: (x[0], x[1], x[2]))
#['big', 'binary', 'cat', 'dog']

L.sort(): 该函数的三个参数和 sorted() 的后三个参数含义是一致的,而需要特别注意的是,该函数只适用于列表,而非任意可以迭代的对象。cmp是比较函数,接受两个对象参数 x 和 y,返回 负数(x<y),0(x=y),正数(x>y)。

student = [['Tom', 'A', 20], ['Jack', 'C', 18], ['Andy', 'B', 11]]
student.sort(key=lambda student: student[2])
  • L.sort() 函数只适用于列表排序,而sorted()函数适用于任意可以迭代的对象排序
  • L.sort()函数排序会改变原有的待排序列表,而sorted()函数则不会改变,而是返回新列表。所以在使用列表进行排序时,需要考虑是否需要保存原列表,如果无需保存原列表,则优先使用L.sort()节省内存空间,提高效率。
  • 两个函数通过定义 key 和 cmp 都可以完成排序,但是 key 的效率要远远高于 cmp,所以要优先使用 key 。cmp传入的函数在整个排序过程中会调用多次,因为会进行多次比较,所以函数调用开销较大,因此使用 key 的效率比 cmp 的效率要高。

7.GIL全局解释器锁

7.1 GIL锁的简介
Python全局解释器锁或GIL是一种互斥锁(或锁),仅允许一个线程持有Python解释器的控制权。意味着在任何时间点只能有一个线程处于执行状态。

7.2 GIL解决什么问题?
Python利用引用计数来进行内存管理,这就意味着在Python中创建的对象都有一个引用计数变量来追踪指向该对象的引用数量。当数量为0时,该对象占用的内存即被释放。而既然使用了计数来进行区分,那就会在多线程中遇到问题:多个线程同时对一个值进行增加/减少的操作。如果发生这种情况,显然会导致非常严重的内存泄漏等问题
因此,为了保证单线程情况下python的正常执行和效率,GIL锁(单一锁)由此产生了,由于只有一个,不会产生死锁且不用切换。但是这的确使每一个计算密集型任务变成了单线程。(伪多线程?)

7.3 为什么选取GIL作为解决方案?
人们针对于C库中那些被Python所需的功能写了许多扩展,为了防止不一致变化,这些C扩展需要线程安全内存管理,而这些正是GIL所提供的。
GIL是非常容易实现而且很容易添加到Python中。因为只需要管理一个锁所以对于单线程任务来说带来了性能提升。

7.4但是为什么GIL没有被删除?
在多线程版本中GIL阻止了计算密集型任务线程并行执行。
GIL对I/O密集型任务多线程程序的性能没有太大的影响,因为在等待I/O时锁可以在多线程之间共享。
但是对于一个线程是完全计算密集型的任务来说(例如,利用线程进行部分图像处理)不仅会由于锁而变成单线程任务而且还会明显的增加执行时间。
这种执行时间的增加是由于锁带来的获取和释放开销。

但是你不能否认GIL带来的单线程性能优势,这也就是为什么Python 3中仍然还有GIL。

7.5 多进程vs多线程:
最流行的方法是应用多进程方法每一个Python进程都有自己的Python解释器和内存空间,因此GIL不会成为问题。multiprocessing模块-多进程
相比于多线程版本,多进程性能有所提升。但是时间并没有下降到之前版本的一半,这是因为进程管理有自己的开销。多进程比多线程更“重”,因此请记住,这可能成为规模瓶颈。

Python GIL,只有当您正在编写C扩展或者您的程序中有计算密集型的多线程任务时才会被GIL影响。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值