6 python高级语法
6.1 GILCpython: C语言写的python解释器,有GIL
jpython: Java语言写的python解释器,没有GIL
面试题如下
描述Python中GIL的概念,以及它对python多线程的影响?编写一个多线程抓取网页的程序,并且阐述多线程抓取程序是否可以比单线程性能有提升,并解释原因.
参考答案
GIL:全局解释器锁,每个线程在执行过程中都需要先获取GIL,保证同一时刻只有一个线程可以执行代码.
但是Python语言与GIL没有半毛钱关系,仅仅是因为历史原因在Cpython虚拟机(解释器),难以移出GIL.
线程释放GIL锁的情况:在IO操作可能会引起阻塞的systen.call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL,python3.x使用计时器(执行时间到达阈值后,当前释放GIL)或者python2.xtickets达到100
计算密集型(无闲时,考虑使用多进程),I/O密集型(有闲时,可以用等待输入的时间来执行其他程序)
克服GIL的方法
更换python的解释器
用其他语言
6.2 深拷贝,浅拷贝1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61"""如果copy.copy()拷贝的是元组,那么它不会进行浅拷贝仅仅是指向原因:因为元组是不可变类型,那么
意味着数据一定不会修改,因此用copy.copy的时候它会自动判断,如果是元组它就指向内存空间"""
import copy
def ():
# b = a
#
# print(id(a))
# print(id(b))
#
# c = copy.deepcopy(a)
# print(id(a))
# print(id(c))
#
# a.append(33)
#
# print(a)
# print(c)
# 约定:当一个变量 = xxxx的时候,变量指向了xxxx
a = [11, 22]
b = [33, 44]
c = [a, b]
d = c
e = copy.copy(c)
# e 指向新的空间
print(id(c))
print(id(e))
print("*" * 50)
# 浅拷贝拷贝了c中的引用
print(id(c[0]))
print(id(e[0]))
print("*" * 50)
a.append(33)
print(c)
print(e)
print("=======>")
# 深拷贝
a = [11, 22]
b = [33, 44]
c = [a, b]
d = c
e = copy.deepcopy(c)
# e 指向新的空间
print(id(c))
print(id(e))
print("*" * 50)
# 深拷贝拷贝拷贝了c中的引用所有的数据
print(id(c[0]))
print(id(e[0]))
print("*" * 50)
a.append(33)
print(c)
print(e)
if __name__ == '__main__':
main()
如果用copy.copy()、copy.deepcopy()对一个全部是不可变类型的数据进行拷贝那么他们结果相同,都是引用指向相同内存空间
如果拷贝的是一个拥有不可变类型的数据,即使元组是最顶层,那么copy.deeocopy()依旧是深拷贝
列表的切片也是d = c[:]与 d = copy.copy(c)一样都是浅拷贝
6.3 私有化,import,封装继承多态度xx:公有变量
_x:私有属性或方法,from module import *禁止导入,类对象和子类可以访问
xx:用户名字空间的魔法对象或属性
xx_:用于避免与Python关键字冲突
6.4 多个模块import的注意点from xx import num
在本模块中创建一个变量指向xx中的num指向内存空间
原来 xx中的num值不会改变
6.5 封装继承多态为什么要封装实际开发中:为了完成较为复杂的任务往往需要多个函数配合,当一个函数收到了数据为了让其他的函数中能够直接使用,很多人想到了使用全局变量来实现传递的功能.但会有些问题:并发程序会从出现对同一个变量操作的问题.将变量+函数制作成一个模板当需要一个功能是就提供一个功能是就提供给需求者.独有的变量 指向模板的引用用来调用函数
继承提升代码的重用率,开发一个类,可以在多个子功能中直接使用.
继承是子类到父类中找代码,不是复制代码
多态
6.6 多继承中的MRO顺序1.单独调用父类的方法
2.super.init()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46# C3算法
class Parent(object):
def __init__(self, name, *args, **kwargs):
print("parent的init开始被调用")
self.name = name
print("parent的init结束被调用")
class Son1(Parent):
def __init__(self, name, age, *args, **kwargs):
print("Son1的init开始被调用")
self.age = age
super().__init__(name, *args, **kwargs)
print("Son1的init结束被调用")
class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs):
print("Son2的init开始被调用")
self.gender = gender
super().__init__(name, *args, **kwargs)
print("Son2的init结束被调用")
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print("Grandson的init开始被调用")
# 多继承时,相对于实用类名.__init___方法, 要把每个父类全部写一遍
# 而super只用一句话,执行全部父类的方法,这也是为何多继承需要全部传参的一个原因
# super(Grandson, self).__init__(name, age, gender)
super().__init__(name, age, gender)
print("Grandson的init结束被调用")
print(Grandson.__mro__)
def ():
pass
if __name__ == '__main__':
main()
# 结果;(, , , , )
# 拿类名到__mor__元组里调用下一个
6.6 * args和 ** kwargs的另外用法: 拆包1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27def test2(a, b, *args, **kwargs):
print("-" * 50)
print(a)
print(b)
print(args)
print(kwargs)
def test1(a, b, *args, **kwargs):
print(a)
print(b)
print(args)
print(kwargs)
# test2(a, b, args, kwargs) # 相当于test2((11, 22, 33, 44, 55, 66), {"name":"zpw", age = 18})
# test2(a, b, *args, kwargs) # 相当于test2(11, 22, 33, 44, 55, 66, {"name":"zpw", age = 18})
test2(a, b, *args, **kwargs) # 相当于test2(11, 22, 33, 44, 55, 66, "name":"zpw", age = 18)
def ():
test1(11, 22, 33, 44, 55, 66, name="zpw", age=18)
if __name__ == '__main__':
main()
6.7 类对象,实例对象,类方法, 实例方法,类属性, 静态方法类也是一个对象,类里面的属性是类属性,init()里的属性是示例属性1.new —-> 创建对象,通俗点说: 有个内存空间
2.init ——> 对刚刚申请的空间进行初始化
3.类属性在内存中只保存一份
4.实力属性在每个对象中都要保存一份
5.通过类创建实例对象是,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份即可.
6.实例方法: 由对象调用:至少一个self参数;执行实例方法时,自动调用该方法的对象赋值给self
7.类方法:由类调用;至少一个cls参数;执行类方法时,自动调用该方法的类赋值给cls
8.静态方法:由类调用;无默认参数;
6.8 property属性一种使用起来像是使用的实例属性一样的特殊属性,可以对应用于某个方法.
property属性内部进行一系列的逻辑计算,最终将计算结果返回.
6.9 property创建方式装饰器:经典类和新式类
类属性;能够简化调用者在获取数据的流程.
6.10 上下文管理器实现了enter()和exit()方法的对象
1
2
3
4
5
6
7
8
9
10from contextlib import contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
with my_open("out.txt", "w") as f:
f.wrirte("hello")