尚硅谷大模型技术之高频面试题
版本:V2.1.9
核心技术 / Python基础

Python基础

19 个问题

数据结构相关#

Qlist 和 tuple 有什么区别?#

列表(list)是可变的,可执行添加、删除或修改元素等操作;元组(tuple)不可变,创建后内容不能更改。且元组能被哈希,可用作字典的键,但列表不行。

闭包相关#

Q什么是闭包?请简述其定义和形成条件。#

闭包是一种特殊的函数嵌套结构,指在外部函数内部定义的内部函数,引用了外部函数的变量(非全局变量),且外部函数返回该内部函数。

形成闭包需满足三个条件:

  • 存在函数嵌套(内部函数定义在外部函数中);
  • 内部函数引用了外部函数的变量;
  • 外部函数返回内部函数的引用。

示例:

Q闭包为什么能保留外部函数的变量?#

正常情况下,函数执行结束后,其内部变量会被销毁。但闭包中,内部函数引用了外部函数的变量,形成了 “变量捕获”,使得这些变量不会随外部函数的结束而销毁,而是被内部函数 “保留” 在内存中,直到内部函数被销毁。

这种机制依赖 Python 的作用域链:内部函数在查找变量时,会先在自身作用域查找,若未找到则向上级(外部函数)作用域查找,闭包通过这种链式查找保留了对外部变量的引用。

Q闭包中如何修改外部函数的变量?nonlocal关键字的作用是什么?#

在闭包中,内部函数默认只能读取外部函数的变量,若直接修改会被 Python 判定为 “定义局部变量”,导致报错。需使用nonlocal关键字声明变量,明确其来自外部函数的作用域,允许内部函数修改。

示例(错误与正确对比):

Q闭包与普通嵌套函数的区别是什么?#

嵌套函数:#

仅指在一个函数内部定义另一个函数,不要求内部函数引用外部变量,也不要求外部函数返回内部函数。例如:

闭包:#

是特殊的嵌套函数,必须满足 “内部函数引用外部变量 + 外部函数返回内部函数”,且内部函数可在外部函数执行完毕后继续访问外部变量。

Q闭包有哪些实际应用场景?请举例说明。#

闭包的核心价值是 “保留状态” 和 “封装数据”,常见应用场景包括:

装饰器(Decorator):#

装饰器本质是闭包,用于在不修改原函数代码的前提下,添加额外功能(如日志、计时)。

数据封装与隐藏:#

模拟类的私有变量,通过闭包隐藏内部状态,只暴露操作接口。

函数工厂:#

动态生成具有不同参数的函数,避免重复代码。

Q闭包可能导致什么问题?如何避免?#

问题:#

闭包会保留外部函数的变量,若变量是大型对象或未及时释放,可能导致内存泄漏(变量长期占用内存不释放)。

避免方式:#
  • 不再使用闭包时,手动将其引用设为None(触发垃圾回收);
  • 避免在闭包中引用过大的对象;
  • 若需频繁创建闭包,考虑用类替代(类的__del__方法可主动释放资源)。

函数相关#

Q什么是 Python decorators?#

装饰器是用于修改函数或类行为的特殊函数。其接收一个函数或类作为输入,并返回新函数或类,常被用于添加日志记录、性能测量等额外功能,用 @ 语法应用于函数或类前。

Qlambda 函数是什么?#

是一种单个表达式的匿名函数,用 lambda 关键字定义,常作为内联函数或用于简单逻辑。例如 add = lambda x, y: x + y 可实现两数相加功能。

Qargs,**kwargs 参数是什么?#

*args 用于向函数传入不确定数量的非关键字参数,接收为元组;**kwargs 用于传入不确定数量的关键字参数,接收为字典。

QPython中如何完成函数的异步调用?#

Python中异步是“单线程非阻塞”的编程模型,核心解决IO密集型任务的阻塞效率问题,核心依赖“事件循环+协程”实现,底层依托生成器的暂停/恢复机制和操作系统的非阻塞IO/IO多路复用(select/epoll等)。

执行逻辑:#
用async/await定义可暂停的协程(异步任务),注册到事件循环;#
事件循环作为总调度,执行协程时,若遇await等待操作,协程暂停并交还执行权,事件循环调度其他可执行协程;#
当等待的IO任务完成,操作系统通知事件循环,协程被标记为可恢复,后续由事件循环调度继续执行;#
直至所有协程执行完毕,事件循环终止。#
特点:#

无线程/进程创建、切换的开销,最大化利用单线程资源;仅适用于IO密集型任务,CPU密集型任务更适合多进程(突破GIL限制)。

内存管理与垃圾回收#

QPython 如何进行内存管理?#

Python 内存由私有堆空间管控,所有对象与数据结构置于此,程序员不能直接访问,内存管理器负责堆空间内存分配。其有内置垃圾收集器,借助对象引用计数等机制,回收未用内存释放给堆空间。

Q什么是垃圾回收?#

Python 为解决内存泄漏,采取对象引用计数,并基于此实现自动垃圾回收。当对象引用计数降为 0 ,内存会被回收。此外,其还处理循环引用等复杂状况。

其他重要问题#

Q解释 Python 中的 GIL(全局解释器锁)?#

GIL 是 Python 解释器的一种机制,其保证任意时刻仅一个线程能执行 Python 字节码。它让 Python 多线程处理 I/O 密集型任务效率尚可,但执行 CPU 密集型任务时,多核心CPU只能发挥和单核CPU一样的性能。

绕过GIL的方式:使用多个进程,每个进程有自己的Python解释器和GIL,然后多进程并行。除此之外还可以使用Cpython或者C扩展,可以释放GIL锁,以及Numpy和SciPy等库也可以不受GIL限制。

注意:Python3.14的发布标志着GIL从强制变成了可选项。

Q如何在 Python 中进行文件删除操作?#

可借助 os 模块的 remove 或 unlink 函数,像 os.remove('filename.txt') 就能删除名为 filename.txt 的文件。

Q什么是Python中的迭代器?#

Python 里的迭代器就是能逐个取元素的对象,得实现__iter__和__next__两个方法,特点是懒加载 —— 用的时候才生成元素特省内存,而且只能往前取、取完就不能复用了。列表、字符串这些可迭代对象,调 iter () 就能转成迭代器,for 循环遍历其实就是自动调 next () 取元素,取完抛的异常会被自动捕获。迭代器主要就是用来处理大数据集,避免一次性加载占满内存的。

Q什么是 Python 中的生成器?#

生成器是实现迭代器的简便方式,为含 yield 表达式的普通函数。其逐个生成值,可暂停和恢复执行,节省内存,适用于处理大序列数据场景。

这里需要注意的是推导式的三个基本的[]列表,{}字典/集合,()生成器表达式,列表和字典推导式不涉及生成器/迭代器,因为这俩都是直接生成完整内容并加载到内存是完整容器,生成器是特殊的迭代器,不会一次性加载所有元素。

Q多进程之间的通信?#

管道(Pipe)#

适用于两个进程之间的双向/单向通信(通常是父子进程或者是兄弟进程)。

特点:基于内存实现,简单高效,无需额外中间介质,但不支持多个进程同时读写(容易发生冲突)

队列(Queue)#

基于“管道 + 锁(多进程内置的互斥锁)”实现,支持多个进程之间的安全通信,是最常用的方法之一。

特点:自带进程安全保护,适合实现“生产者-消费者”模型,使用简单,无需手动处理锁冲突。

共享内存(Value/Array)#

让多个进程共享同一块内存区域,直接读写内存数据,速度最快。

特点:高效适合大量数据传输,但无内置锁保护,需要手动加锁(如Lock)避免并发读写冲突。

管理器(Manager)#

基于“服务进程 + 客户端代理”实现,自带进程安全锁,底层依托本地网络套接字通信。支持共享复杂数据结构(字典,列表等),可以跨多个进程甚至跨机使用。

特点:功能强大,使用灵活,但基于网络通信,效率比共享内存低,适合需要共享复杂数据的场景。

补充:信号/事件(同步辅助)#

不传输实际数据,主要用于进程间同步(通知对方如状态变化,如“数据已准备好”)

特点:常配合其他通信方法使用,协调进程执行顺序,而非传递业务数据。

Q多个线程之间的通信?#

一般常用的有:直接共享全局变量/对象,实现简单但是没有内置线程安全保护,并发易发生错误,得手动加锁。队列方法和多进程队列类似,内置线程互斥锁,无需手动加锁,最常用。同步原语(Event/Condition)用于协调线程通信,不直接传递业务数据。线程之间通信的核心优势是共享进程内存,比进程通信简单高效。