99%的Python面试一定会问道的这5个问题,你学废了吗?
前面的Python课程大家可以通过下面链接打开。
前面的6个课程基本上覆盖了Python的基础,今天我们重点跟大家分享下Python的一些典型的面试问题,大家可以看看。
第一个问题:介绍下Python的多线程吧,以及Python的多线程有什么问题?
回答:Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。所以虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。在多线程环境中,Python 虚拟机按以下方式执行:a、设置 GIL;b、切换到一个线程去运行;c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));d、把线程设置为睡眠状态;e、解锁 GIL;d、再次重复以上所有步骤。
所以,Python的多线程并不是真正意义上的多线程;
那么Python的多线程是不是完全没有意义呢?也不是,多线程能够将不同的任务隔离开来,同时,如果是IO型密集型的程序,多线程也是能够发挥作用的(CPU密集型就比较鸡肋了);
第二个问题:简要描述Python的垃圾回收机制
Python中的垃圾回收是以引用计数为主,标记-清除和分代回收为辅。
引用计数:Python在内存中存储每个对象的引用计数,如果计数变成0,该对象就会消失,分配给该对象的内存就会释放出来。
如果一些无法用引用计数回收的对象,那么就用标记-清除和分代回收的方式
标记-清除:一些容器对象,比如list、dict、tuple,instance等可能会出现引用循环,对于这些循环,垃圾回收器会定时回收这些循环(对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边)。
分代收集:Python把内存根据对象存活时间划分为三代,对象创建之后,垃圾回收器会分配它们所属的代。每个对象都会被分配一个代,而被分配更年轻的代是被优先处理的,因此越晚创建的对象越容易被回收。
第三个问题:聊聊Python的深拷贝和浅拷贝;
Python中对象之间的赋值是按引用传递的,如果要拷贝对象需要使用标准模板中的copy
copy.copy:浅拷贝,只拷贝父对象,不拷贝父对象的子对象。
copy.deepcopy:深拷贝,拷贝父对象和子对象。
具体详细的细节可以看上一篇文章;这里建议大家实操下,增加印象,后面面试官需要举例子的时候,也能够很快的举例出来;
第四个问题:聊聊装饰器(感觉80%的Python面试都会碰到),以及能不能实际举个装饰器的例子;
回答:装饰器本质上是一个Python函数,它可以让其它函数在不作任何变动的情况下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景。比如:插入日志、性能测试、事务处理、缓存、权限校验等。有了装饰器我们就可以抽离出大量的与函数功能无关的雷同代码进行重用。
装饰器的例子(例子来自网上):
def timer(func):
def wrapper():
start = time.time()
func()
end = time.time()
print(end-start)
# 保留四位有效数字
print('total time:{:.4}'.format(end-start))
return wrapper
@timer
def step1():
count = 0
for i in range(1000000):
count+=1
@timer
def step2():
count = 0
for i in range(12000000):
count+=1
step1()
step2()
装饰器的代码大家一定要多操作几次,并且彻底掌握,包括闭包的概念;这里就不详细说明了
第五个问题:介绍下Python的迭代器和生成器(yield),并且实际举个例子;
迭代器和生成器都是Python中特有的概念,迭代器可以看作是一个特殊的对象,每次调用该对象时会返回自身的下一个元素,从实现上来看,一个可迭代的对象必须是定义了__iter__()方法的对象,而一个迭代器必须是定义了__iter__()方法和next()方法的对象。
举例:
> list1
[1, 2, 3, 4, 5, 'hello', 'runfor', ['hello', 12, 'runfor'], [...]]
> iterator = iter(list1)
> next(iterator)
1
> next(iterator)
2
>
大家可以看到,list的容器就有iter方法,而iter方法就是返回一个迭代器;
生成器算是一种特殊的迭代器,因为生成器是能够返回一个迭代器的函数(通过yield),其最大的作用是将输入对象返回为一个迭代器。
举例(关于yield大家需要进一步学习下,确保能够完全掌握):
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 使用 yield
a, b = b, a + b
n = n + 1
for n in fab(5):
print n
使用迭代器的好处是什么呢?
Python中使用了迭代的概念,是因为当需要循环遍历一个较大的对象时,传统的内存载入方式会消耗大量的内存,通过迭代器能够节省空间。