page contents

Python 面试进阶题:答不上 GIL,前面全白答

基础题靠背能过,进阶题靠背过不去。下面这 6 道题是区分"真写过项目"和"只看过教程"的分水岭。第一道 GIL 答不上来,后面全白搭。

attachments-2026-10-6kDFvPvb6ad572d0ccda2.png

基础题靠背能过,进阶题靠背过不去。下面这 6 道题是区分"真写过项目"和"只看过教程"的分水岭。第一道 GIL 答不上来,后面全白搭。

01

 

GIL 是什么?它有什么影响?

 

 

答案:GIL(全局解释器锁)保证同一时刻只有一个线程在解释器里执行 Python 字节码。

•CPU 密集型任务:多线程无法并行,用多进程(multiprocessing)。

•I/O 密集型任务:多线程有效(等待时释放 GIL)。

•补充:Python 3.13+ 提供 free-threading 构建(无 GIL);3.15 里是可选安装,标准默认构建仍带 GIL。

为什么这么考:GIL 是面试里区分"真懂线程模型"和"背概念"的一道题。答完基本盘,面试官会追两句:

•追"CPU 密集怎么办"——答多进程;追"I/O 密集呢"——答多线程有效,等待时让出 GIL。

•再追"GIL 什么时候释放"——答 I/O 阻塞时释放;平时解释器按时间片轮流让出(sys.setswitchinterval,默认 5 毫秒)做线程切换。

•能主动补一句"free-threading 只是可选构建,默认装出来还是带 GIL 的版本",这题就答扎实了。

真实面试里这题常被追问到细节:"GIL 在 C 扩展里能不能释放?"——能,写 C 扩展时用 Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS 宏可以临时释放 GIL,让其他线程在 C 代码执行期间跑起来。numpy 的矩阵运算、io 操作的底层实现都是这么做的。能说出这一点,说明你看过 C 扩展或者读过相关源码。

02

 

装饰器原理是什么?为什么加 functools.wraps?

 

 

   python

import functools

def deco(func):
    @functools.wraps(func)     # 把原名、文档复制给 wrapper
    def wrapper(*args, **kwargs):
        print("before")
        res = func(*args, **kwargs)
        print("after")
        return res
    return wrapper

@deco
def hello():
    """打招呼"""
    print("hello")

hello.__name__   # 'hello'  ← 有 wraps 才是

答案:装饰器是"接收函数、返回新函数"的闭包。@deco 就是把 hello 替换成 wrapper。不加 functools.wraps,被装饰函数的名字、文档都会变成 wrapper 的。

为什么这么考:光会写装饰器,答不出 wraps 就露馅了——这正是"真写过"和"看过教程"的分界线。面试官常顺着追问:

•问 wraps 复制了什么——答 __name__、__doc__ 这些元信息;

•问装饰器什么时候执行——答模块导入、函数定义那一刻就执行;

•问两个装饰器叠一起怎么执行——答自下往上执行。

03

 

生成器是什么?和列表推导式有什么区别?

 

 

   python

# 列表:一次性全算出来,占内存
lst = [x * x for x in range(10_000_000)]

# 生成器:用的时候才算,省内存
gen = (x * x for x in range(10_000_000))

sum(gen)     # 逐个产出

答案:生成器是惰性求值的迭代器,用 yield 逐个产出,不一次全算。生成器表达式用小括号。

为什么这么考:大数据场景里,省不省内存就看这道题。答出"惰性"两个字先过关,剩下的追问集中在这几点:

•生成器能不能重复遍历——答不能,for 一遍就耗尽,要再用得重建;

•yield 和 return 什么关系——答函数体里出现 yield 就是生成器函数,return 表示迭代结束;

•再深一点会问 send()——答它能把值传回生成器里继续跑。答到这个程度,说明真用过。

04

 

迭代器和可迭代对象有什么区别?

 

 

   python

lst = [1, 2, 3]        # 可迭代对象
it = iter(lst)         # 迭代器:有 __next__,有状态
next(it)    # 1
next(it)    # 2

答案:可迭代对象能被 for 遍历(有 __iter__);迭代器有 __next__,逐个取值、取完耗尽。for 循环底层就是不断 next()。

为什么这么考:这是生成器的前置题,考你对 for 的底层理解。常见追问:

•生成器是迭代器吗——答是,它既是可迭代对象也是迭代器;

•list 是迭代器吗——答不是,list 有 __iter__ 没有 __next__;

•为什么 for 能直接遍历 list——答 for 先调 iter() 拿到迭代器,再循环 next() 直到抛 StopIteration。

05

 

垃圾回收是怎么工作的?

 

 

 

答案:Python 主要用引用计数回收内存,对象引用数归零立刻回收。循环引用(两个对象互相引用)会骗过引用计数,由 gc 模块的"标记-清除"周期性处理。

   python

import gc
gc.collect()   # 手动触发一次

为什么这么考:考你"内存谁在管"——这是区分脚本仔和工程咖的题。答完这句,面试官会再挖一层:

•什么时候要手动 gc.collect()——答产生大量循环引用、内存又吃紧的时候;

•循环引用怎么来的——答两个对象互相持有对方引用最常见,比如类实例互相引用;

•能补一句"引用计数为主、gc 只是兜循环引用",说明你是真理解内存管理。

06

 

经典陷阱合集:这几道一次答全

 

 

   python

# 1. 布尔是 int 的子类
True == 1        # True
True + True      # 2

# 2. dict 遍历时删除 → 报错
d = {"a": 1, "b": 2}
for k in d:
    del d[k]     # RuntimeError: dictionary changed size during iteration

# 3. 字符串乘法
"ab" * 3         # 'ababab'

# 4. list 为什么不能当 dict 的 key
d = {[1, 2]: "x"}   # TypeError: unhashable type: 'list'

为什么这么考:这些不是"考不考得到"的问题,是"面试官随手写一行看你反应"的问题。答不上来,前面全白答。每道都能再追问一层:

•布尔为什么能相加——答 bool 继承自 int,True 就是 1;

•遍历删除为什么报错——答迭代期间 dict 的大小变了;

•list 为什么不能当 key——答可变对象不可哈希,想用就用 tuple。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

  • 发表于 2026-10-19 09:31
  • 阅读 ( 25 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2541 篇文章

作家榜 »

  1. Pack 2541 文章
  2. 轩辕小不懂 2403 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 216 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章