Day 98:【99天精通Python】常见面试题解析 (中) - 函数、面向对象与并发
前言
欢迎来到第98天!
在昨天的课程中,我们复习了 Python 的基础语言特性和数据结构。今天,我们将难度升级,进入面试中更能体现候选人内功的领域:函数式编程、面向对象和并发。
这些问题不仅考验你对语法的熟练度,更考验你对编程思想和设计模式的理解。
本节内容:
- 函数:闭包、装饰器、
*args&**kwargs - 面向对象:
__new__vs__init__、MRO - 并发:多线程 vs 多进程 vs 协程
一、函数式编程
1. 问:什么是闭包 (Closure)?它有什么用?
参考回答:
- 是什么: 闭包是指一个内部函数记住了并可以访问其外部函数作用域中的变量,即使外部函数已经执行完毕。
- 三大要素:
- 函数嵌套。
- 内部函数引用了外部函数的变量。
- 外部函数返回了内部函数。
- 有什么用:
- 数据封装: 可以将数据私有化,只通过返回的函数来访问。
- 延迟计算: 返回的函数可以在未来的某个时刻被调用。
- 装饰器的基础: 装饰器就是闭包的一种经典应用。
示例:
defouter(x):definner(y):returnx+yreturninner add_5=outer(5)# x=5 被 inner 记住了print(add_5(10))# 152. 问:请解释一下 Python 装饰器 (Decorator) 的原理。
参考回答:
- 本质: 装饰器本质上是一个接受函数作为参数,并返回一个新函数的高阶函数。它利用了闭包的特性。
- 作用: 在不修改原函数代码的前提下,动态地给函数增加额外的功能(如日志、计时、权限验证)。
- 原理:
@my_decorator这种语法糖,等价于my_func = my_decorator(my_func)。- 装饰器函数内部通常会定义一个
wrapper函数,在这个wrapper内部执行"前置操作" -> 调用原函数 -> 执行"后置操作"。 - 最后返回这个
wrapper函数。
示例:
deftimer(func):defwrapper(*args,**kwargs):# 前置start=time.time()# 调用原函数result=func(*args,**kwargs)# 后置print(f"耗时:{time.time()-start}")returnresultreturnwrapper@timerdefsome_task():time.sleep(1)3. 问:*args和**kwargs是什么?
参考回答:
它们是 Python 中处理函数可变参数的语法。
*args(Arguments):- 用于接收任意数量的位置参数。
- 在函数内部,
args是一个元组 (tuple)。 - 示例:
def func(a, *args),调用func(1, 2, 3)时,a=1,args=(2, 3)。
**kwargs(Keyword Arguments):- 用于接收任意数量的关键字参数。
- 在函数内部,
kwargs是一个字典 (dict)。 - 示例:
def func(**kwargs),调用func(name='a', age=10)时,kwargs={'name': 'a', 'age': 10}。
顺序: 函数定义时,参数顺序必须是:普通参数 -> *args -> 默认参数 -> **kwargs。
二、面向对象 (OOP)
4. 问:__new__和__init__的区别?
参考回答:
__new__: 是一个类方法,是真正创建实例的静态方法。它必须返回一个实例对象。__init__: 是一个实例方法,在实例创建好之后,负责初始化实例的属性。它不返回任何东西。- 执行顺序: 先
__new__,后__init__。__new__创建并返回实例,这个实例作为self传给__init__。
应用场景:
- 绝大多数情况我们只用
__init__。 __new__主要用于实现一些高级功能,比如单例模式,或者继承不可变类型 (如tuple,str)。
5. 问:什么是 MRO (Method Resolution Order)?
参考回答:
- 是什么: MRO 是方法解析顺序。它定义了在多重继承中,当一个方法被调用时,Python 查找这个方法的顺序。
- 算法: Python 3 使用C3 线性化算法来确定 MRO。它保证了顺序的单调性和局部优先。
- 如何查看: 可以通过
ClassName.__mro__或ClassName.mro()查看。
示例 (菱形继承):
classA:passclassB(A):passclassC(A):passclassD(B,C):passprint(D.__mro__)# 输出: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)# 顺序是 D -> B -> C -> A三、并发编程
6. 问:请对比多线程、多进程和协程的优缺点及适用场景。
参考回答:
| 特性 | 多线程 (Threading) | 多进程 (Multiprocessing) | 协程 (AsyncIO) |
|---|---|---|---|
| 优点 | 资源开销小,线程间共享内存 | 能利用多核 CPU,真正并行 | 开销极小,单线程实现高并发 |
| 缺点 | 受 GIL 限制,无法并行计算 | 资源开销大,进程间通信复杂 | 不能用阻塞代码,生态不完善 |
| 适用场景 | I/O 密集型(爬虫, Web) | CPU 密集型(计算, 视频处理) | 超高并发 I/O(Web服务器, IM) |
四、如何回答开放性问题
面试官可能会问:“请设计一个秒杀系统”。
回答思路:
- 明确需求: 秒杀的核心是什么?(超高并发、防止超卖)。
- 架构设计:
- 前端: 点击按钮后禁用,防止重复提交。
- 后端:
- 流量削峰: 用 Nginx/CDN 挡住大部分无效流量。
- 库存预减: 用Redis代替数据库处理库存(原子操作
decr)。 - 异步下单: 将抢到资格的用户请求放入消息队列 (如 Celery + RabbitMQ),后台 Worker 慢慢处理数据库订单。
- 总结: 核心是将请求分层过滤,把热点数据的读写从数据库转移到内存 (Redis),把同步操作变为异步操作。
系列导航:
- 上一篇:Day 97 - 常见面试题解析 (上)
- 下一篇:Day 99 - 常见面试题解析 (下) & 毕业感言(待更新)