你简历上写精通Python。面试官看了一眼,抬头问你,Python的GIL是什么。你能说清楚吗,还是只会背“全局解释器锁”六个字。很多人卡在这一关,因为真问下去就露馅了。
GIL是CPython解释器里的一个互斥锁。它不让多个线程同时执行Python字节码。你有八个核,开了八个线程。跑CPU密集任务,只有一个核心在工作。其他七个在等锁。这就是GIL的直观效果。
面试官接着问,那GIL怎么解决。你说用多进程。对,但还不够。多进程要序列化对象,用pickle传输数据。内存开销大。还有别的办法吗。用C扩展,像Cython写一段代码释放GIL。用asyncio处理IO密集型任务,协程在单线程里切换开销极小。这才能体现你真的思考过。
第二个拷问,Python里的列表和元组底层有什么区别。你别说一个可变一个不可变。那谁都知道。列表底层是动态数组,连续内存存储指针。元组是固定数组,不能扩容。所以列表有resize操作,动态扩容时申请新内存复制旧数据。元组一次性分配完成。元组访问比列表略快,因为少了检查resize的逻辑。
面试官追问,那元组不可变,为什么你可以在元组里放一个列表。你答,元组存的是引用不可变,引用指向的对象内容可变。这个问题的坑是“不可变”的边界。你答清楚,证明你理解Python对象模型。
第三个,装饰器是怎么工作的。别只背语法糖。装饰器本质上是一个函数,接受被装饰函数作为参数,返回一个新函数。最核心的是闭包,内层函数记住了外层函数的变量。面试官让你手写一个带参数的装饰器,比如重复执行n次。你写出来了吗。
第四,Python里的可变对象和不可变对象传参是值传递还是引用传递。这是大坑。你答引用传递。面试官说那为什么函数里改int变量外面不生效。因为int是不可变对象。你对参数重新赋值,其实是让变量指向一个新对象。外部变量还在原来对象上。Python传参全传对象引用,不是传变量名。明白这个才能解释清楚。
第五,生成器和迭代器的区别是什么。迭代器是实现了__iter__和__next__的对象。生成器是用yield写的函数,每次调用next才往下执行。面试官让你解释yield from。这玩意内部做了循环加异常捕获,能代理另一个生成器的产出。你用过吗。
第六,深拷贝和浅拷贝的区别。浅拷贝复制对象引用,深拷贝递归复制所有内容。Python里copy.deepcopy用了备忘录模式,记录已经复制的对象,防止循环引用死循环。这个机制你清楚吗。
第七,with语句的上下文管理协议是什么。你回答__enter__和__exit__。面试官问你如果这个对象没实现这两个方法,还能用with吗。不能。你再深入一点,contextlib.contextmanager装饰器可以把一个生成器函数变成上下文管理器。里面的yield分割了进入和退出逻辑。这很实用。
第八,Python里闭包的内存泄漏是怎么回事。你写一个函数返回一个闭包,闭包里引用了外部函数的大对象。这个闭包活得久,大对象就一直不能被回收。js里有经典的循环引用问题,Python里也有。用weakref可以解决。
第九,元类的实际用途。你别说用来创建类。那谁都知道。面试官要听例子。ORM里用元类把数据库表定义映射成类。用元类控制类创建过程,比如自动注册子类,或者禁止某些属性。Django的ModelBase就是元类。你能讲出这些,说明不是背概念。
第十,描述符是什么。__get__、__set__、__delete__三个方法。你用过@property,它底层就是描述符协议。面试官让你自己实现一个描述符做类型校验,比如只能赋整数的属性。你写一个类实现__set__,在set里检查type(val) == int,不匹配就抛TypeError。描述符控制属性访问,是Python黑魔法的根基。能讲清楚这个,面试官会对你刮目相看。
这些问题的共同点是不考你记了多少东西,考你对语言运行时的理解。面试官听太多人背答案了。你讲出底层原理,讲出实际应用场景,讲出你踩过的坑。这才是精通。别写完代码就跑,回头把这些问题过一遍,比刷十道算法题管用。