🐍
Python 面试题
25 道题 · 4 个模块
简单 8中等 12困难 5
基础语法
7 题Q1.Python 中 is 和 == 的区别是什么?简单
Q1.Python 中 is 和 == 的区别是什么?
参考答案
`is` 比较两个对象的**内存地址**(身份),即是否是同一个对象。`==` 比较两个对象的**值**是否相等。例如 `a = [1,2]; b = [1,2]; a == b` 为 True,但 `a is b` 为 False。小整数和短字符串由于 Python 的 intern 机制可能例外。Q2.解释 Python 中的可变类型和不可变类型简单
Q2.解释 Python 中的可变类型和不可变类型
参考答案
**不可变类型**:int、float、str、tuple、frozenset——修改时会创建新对象。**可变类型**:list、dict、set——可以原地修改。区别影响函数传参:不可变类型作为函数参数时,函数内修改不会影响外部;可变类型会影响。Q3.*args 和 **kwargs 是什么?简单
Q3.*args 和 **kwargs 是什么?
参考答案
`*args` 将不定数量的**位置参数**打包成 tuple。`**kwargs` 将不定数量的**关键字参数**打包成 dict。
python
def func(*args, **kwargs):
print(args) # (1, 2, 3)
print(kwargs) # {'name': '小明'}
func(1, 2, 3, name='小明')
传递时 `*` 和 `**` 用于解包。Q4.Python 中的浅拷贝和深拷贝简单
Q4.Python 中的浅拷贝和深拷贝
参考答案
`copy.copy()` 创建**浅拷贝**——只复制外层对象,内部嵌套对象还是共享引用。`copy.deepcopy()` 创建**深拷贝**——递归复制所有嵌套对象。
python
import copy
a = [[1,2], [3,4]]
b = copy.copy(a) # 浅:b[0] is a[0] → True
c = copy.deepcopy(a) # 深:c[0] is a[0] → False
Q5.lambda 函数是什么?什么时候使用?简单
Q5.lambda 函数是什么?什么时候使用?
参考答案
`lambda` 是创建小型匿名函数的语法:`lambda 参数: 表达式`。适合 `map()`、`filter()`、`sorted()` 的 key 参数等一次性使用场景。
python
sorted(data, key=lambda x: x['age'])
add = lambda a, b: a + b
但 lambda 只能包含一个表达式——复杂逻辑用 `def`。Q6.Python 中的异常处理 try/except/finally?简单
Q6.Python 中的异常处理 try/except/finally?
参考答案
python
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f'除零错误: {e}')
except (TypeError, ValueError) as e:
print(f'类型/值错误: {e}')
except Exception as e:
print(f'其他错误: {e}')
else:
print('没有异常时执行')
finally:
print('无论是否异常都执行——用于清理资源')
`raise` 抛出异常,`raise from` 异常链。Q7.Python 中 `if __name__ == '__main__'` 的作用?简单
Q7.Python 中 `if __name__ == '__main__'` 的作用?
参考答案
当 Python 文件被**直接运行**时,`__name__` 变量的值为 `'__main__'`。当文件被**作为模块导入**时,`__name__` 为模块名。
python
# mymodule.py
def main():
print('直接运行时执行')
if __name__ == '__main__':
main() # 只在直接运行时执行
这是 Python 模块既可导入又可独立运行的惯用写法。进阶特性
9 题Q1.什么是装饰器?写一个计时装饰器中等
Q1.什么是装饰器?写一个计时装饰器
参考答案
装饰器是一个接受函数作为参数并返回新函数的可调用对象。用于在不修改原函数的情况下添加功能。
python
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f'{func.__name__} 耗时: {time.time()-start:.2f}s')
return result
return wrapper
@timer
def slow_func():
time.sleep(1)
Q2.解释 Python 的 GIL(全局解释器锁)中等
Q2.解释 Python 的 GIL(全局解释器锁)
参考答案
GIL 是 CPython 中的互斥锁,确保同一时刻只有一个线程执行 Python 字节码。这意味着多线程在 CPU 密集型任务中不能利用多核。
- **IO 密集型**(网络/文件):多线程有效(等待 IO 时释放 GIL)
- **CPU 密集型**:用 `multiprocessing` 替代 `threading`,或用 C 扩展绕过 GILQ3.生成器 generator 和列表的区别?yield 怎么用?中等
Q3.生成器 generator 和列表的区别?yield 怎么用?
参考答案
生成器是**惰性求值**的迭代器——只在需要时产生下一个值,不一次性把所有数据加载到内存。`yield` 暂停函数执行并返回值,下次调用 `next()` 时从暂停处继续。
python
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
适合处理大数据流、无限序列、流水线处理。Q4.with 语句的原理?上下文管理器怎么实现?中等
Q4.with 语句的原理?上下文管理器怎么实现?
参考答案
`with` 语句调用对象的 `__enter__` 和 `__exit__` 方法,确保资源正确释放。
python
class FileManager:
def __init__(self, filename, mode):
self.file = open(filename, mode)
def __enter__(self):
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
return False # 不抑制异常
with FileManager('test.txt', 'w') as f:
f.write('Hello')
也可用 `contextlib.contextmanager` 装饰器简化。Q5.Python 的垃圾回收机制是怎样的?困难
Q5.Python 的垃圾回收机制是怎样的?
参考答案
CPython 主要使用**引用计数**:每个对象有一个计数器,为 0 时立即回收。
- 优点:即时回收,确定性
- 问题:无法处理**循环引用**(A→B, B→A)
**分代回收**作为补充:将对象分为三代(0/1/2),新生对象在 0 代,存活越久代数越高。GC 更频繁扫描低代对象。用 `gc` 模块可手动控制。Q6.Python 多线程 vs 多进程?什么时候用哪个?中等
Q6.Python 多线程 vs 多进程?什么时候用哪个?
参考答案
**多线程**:共享内存,受 GIL 限制——同一时刻只有一个线程执行 Python 代码。适合 IO 密集型任务(网络请求、文件读写)。
**多进程**:每个进程有独立的 Python 解释器和 GIL。适合 CPU 密集型任务(计算、图像处理)。
python
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# IO 密集型 → ThreadPoolExecutor
# CPU 密集型 → ProcessPoolExecutor
Q7.Python 中的元类(metaclass)是什么?困难
Q7.Python 中的元类(metaclass)是什么?
参考答案
元类是**创建类的类**——类也是对象,元类控制类的创建过程。`type` 是默认元类。
python
class MyMeta(type):
def __new__(cls, name, bases, dct):
dct['created_by'] = 'MyMeta'
return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=MyMeta):
pass
print(MyClass.created_by) # 'MyMeta'
常用场景:ORM(Django/SQLAlchemy)、API 校验、自动注册子类。Q8.Python 的 asyncio 工作原理?与多线程的区别?困难
Q8.Python 的 asyncio 工作原理?与多线程的区别?
参考答案
asyncio 使用**事件循环 + 协程**——单线程内协作式多任务。遇到 `await` 时挂起当前协程,让出控制权。
与多线程的区别:
- asyncio 单线程——无锁竞争、无上下文切换开销
- 线程由操作系统抢占调度——asyncio 由开发者显式 await 调度
- asyncio 适合高并发 IO(数万连接),线程适合中等并发
python
import asyncio
async def fetch(url):
async with aiohttp.ClientSession() as s:
async with s.get(url) as r:
return await r.text()
async def main():
tasks = [fetch(url) for url in urls]
results = await asyncio.gather(*tasks)
Q9.Python 的 slots 是什么?什么时候用?中等
Q9.Python 的 slots 是什么?什么时候用?
参考答案
`__slots__` 限制实例只能有指定的属性——阻止动态添加属性,节省内存(不用 `__dict__`)。
python
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x; self.y = y
p = Point(1, 2)
# p.z = 3 # ❌ AttributeError
适用场景:创建大量小对象时(如数据处理、游戏开发),可节省 40-50% 内存。数据结构
5 题Q1.list 和 tuple 的区别?什么时候用哪个?简单
Q1.list 和 tuple 的区别?什么时候用哪个?
参考答案
`list` 可变(可增删改),`tuple` 不可变(创建后不能修改)。
- **用 tuple**:作为字典的 key、函数返回多个值、不需要修改的数据(更快更省内存)
- **用 list**:需要增删元素、动态变化的数据Q2.字典的底层实现原理?为什么查找这么快?中等
Q2.字典的底层实现原理?为什么查找这么快?
参考答案
Python 3.6+ 字典使用**紧凑哈希表**实现。
1. 对 key 计算 hash 值
2. 用 hash 值找到在哈希表中的位置
3. 平均 O(1) 时间复杂度
Python 3.6 后使用两个数组:一个存储索引(稀疏),一个密集存储条目——节省内存且保持插入顺序。Q3.列表推导式、生成器表达式、字典推导式的区别和适用场景?中等
Q3.列表推导式、生成器表达式、字典推导式的区别和适用场景?
参考答案
python
# 列表推导式——立即求值,返回 list(占内存)
[x*2 for x in range(1000000)] # 占用~8MB
# 生成器表达式——惰性求值,返回 generator(省内存)
(x*2 for x in range(1000000)) # 几乎不占内存
# 字典推导式
{x: x*2 for x in range(10)}
# 场景:小数据用列表推导,大数据流用生成器
sum(x*2 for x in range(1000000)) # 推荐
Q4.Python 中的 collections 模块有哪些常用数据结构?中等
Q4.Python 中的 collections 模块有哪些常用数据结构?
参考答案
- **namedtuple**:有名字的 tuple,可读性好
- **deque**:双端队列,两端 O(1) 操作
- **Counter**:计数器,自动统计频次
- **OrderedDict**:有序字典(Python 3.7+ 内置 dict 已有序)
- **defaultdict**:带默认值的字典
- **ChainMap**:多个字典组合查找
python
from collections import Counter, defaultdict, deque
c = Counter(['a','b','a','c','a']) # {'a':3, 'b':1, 'c':1}
d = defaultdict(list); d['k'].append(1)
q = deque([1,2,3]); q.appendleft(0)
Q5.列表、集合、字典的查找时间复杂度?中等
Q5.列表、集合、字典的查找时间复杂度?
参考答案
| 数据结构 | 查找 | 插入 | 删除 |
|----------|------|------|------|
| list | O(n) | O(1)* | O(n) |
| set | O(1) | O(1) | O(1) |
| dict | O(1) | O(1) | O(1) |
*末尾插入 O(1),头部插入 O(n)
**关键结论**:需要频繁 `in` 判断时,用 set/dict 而非 list。算法题
4 题Q1.实现一个 LRU 缓存(最近最少使用)困难
Q1.实现一个 LRU 缓存(最近最少使用)
参考答案
python
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key: int) -> int:
if key not in self.cache:
return -1
self.cache.move_to_end(key) # 标记为最近使用
return self.cache[key]
def put(self, key: int, value: int) -> None:
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False) # 移除最旧
Q2.反转一个字符串的多种方法中等
Q2.反转一个字符串的多种方法
参考答案
python
s = "hello"
# 1. 切片(最 Pythonic)
s[::-1] # 'olleh'
# 2. reversed + join
''.join(reversed(s))
# 3. 循环
''.join(s[i] for i in range(len(s)-1, -1, -1))
# 4. reduce
from functools import reduce
reduce(lambda x, y: y + x, s)
Q3.如何实现一个单例模式(Singleton)?中等
Q3.如何实现一个单例模式(Singleton)?
参考答案
python
# 方法1:使用 __new__
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 方法2:使用装饰器
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
# 方法3:使用模块(最简单)
# Python 模块天然是单例的
Q4.如何在 Python 中实现一个线程安全的计数器?困难
Q4.如何在 Python 中实现一个线程安全的计数器?
参考答案
python
import threading
# 方法1:threading.Lock
class Counter:
def __init__(self):
self._count = 0
self._lock = threading.Lock()
def increment(self):
with self._lock:
self._count += 1
# 方法2:使用 queue.Queue 传递消息
# 方法3:multiprocessing.Value(多进程安全)
from multiprocessing import Value
counter = Value('i', 0)
with counter.get_lock():
counter.value += 1