Z
ZHANK
🐍

Python 面试题

25 道题 · 4 个模块

简单 8中等 12困难 5

基础语法

7
Q1.Python 中 is 和 == 的区别是什么?
简单
参考答案
`is` 比较两个对象的**内存地址**(身份),即是否是同一个对象。`==` 比较两个对象的**值**是否相等。例如 `a = [1,2]; b = [1,2]; a == b` 为 True,但 `a is b` 为 False。小整数和短字符串由于 Python 的 intern 机制可能例外。
Q2.解释 Python 中的可变类型和不可变类型
简单
参考答案
**不可变类型**:int、float、str、tuple、frozenset——修改时会创建新对象。**可变类型**:list、dict、set——可以原地修改。区别影响函数传参:不可变类型作为函数参数时,函数内修改不会影响外部;可变类型会影响。
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 中的浅拷贝和深拷贝
简单
参考答案
`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 函数是什么?什么时候使用?
简单
参考答案
`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?
简单
参考答案
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__'` 的作用?
简单
参考答案
当 Python 文件被**直接运行**时,`__name__` 变量的值为 `'__main__'`。当文件被**作为模块导入**时,`__name__` 为模块名。

python
# mymodule.py
def main():
    print('直接运行时执行')

if __name__ == '__main__':
    main()  # 只在直接运行时执行

这是 Python 模块既可导入又可独立运行的惯用写法。

进阶特性

9
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(全局解释器锁)
中等
参考答案
GIL 是 CPython 中的互斥锁,确保同一时刻只有一个线程执行 Python 字节码。这意味着多线程在 CPU 密集型任务中不能利用多核。
- **IO 密集型**(网络/文件):多线程有效(等待 IO 时释放 GIL)
- **CPU 密集型**:用 `multiprocessing` 替代 `threading`,或用 C 扩展绕过 GIL
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 语句的原理?上下文管理器怎么实现?
中等
参考答案
`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 的垃圾回收机制是怎样的?
困难
参考答案
CPython 主要使用**引用计数**:每个对象有一个计数器,为 0 时立即回收。
- 优点:即时回收,确定性
- 问题:无法处理**循环引用**(A→B, B→A)

**分代回收**作为补充:将对象分为三代(0/1/2),新生对象在 0 代,存活越久代数越高。GC 更频繁扫描低代对象。用 `gc` 模块可手动控制。
Q6.Python 多线程 vs 多进程?什么时候用哪个?
中等
参考答案
**多线程**:共享内存,受 GIL 限制——同一时刻只有一个线程执行 Python 代码。适合 IO 密集型任务(网络请求、文件读写)。

**多进程**:每个进程有独立的 Python 解释器和 GIL。适合 CPU 密集型任务(计算、图像处理)。

python
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# IO 密集型 → ThreadPoolExecutor
# CPU 密集型 → ProcessPoolExecutor
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 工作原理?与多线程的区别?
困难
参考答案
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 是什么?什么时候用?
中等
参考答案
`__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 的区别?什么时候用哪个?
简单
参考答案
`list` 可变(可增删改),`tuple` 不可变(创建后不能修改)。
- **用 tuple**:作为字典的 key、函数返回多个值、不需要修改的数据(更快更省内存)
- **用 list**:需要增删元素、动态变化的数据
Q2.字典的底层实现原理?为什么查找这么快?
中等
参考答案
Python 3.6+ 字典使用**紧凑哈希表**实现。
1. 对 key 计算 hash 值
2. 用 hash 值找到在哈希表中的位置
3. 平均 O(1) 时间复杂度

Python 3.6 后使用两个数组:一个存储索引(稀疏),一个密集存储条目——节省内存且保持插入顺序。
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 模块有哪些常用数据结构?
中等
参考答案
- **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.列表、集合、字典的查找时间复杂度?
中等
参考答案
| 数据结构 | 查找 | 插入 | 删除 |
|----------|------|------|------|
| 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 缓存(最近最少使用)
困难
参考答案
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.反转一个字符串的多种方法
中等
参考答案
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)?
中等
参考答案
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 中实现一个线程安全的计数器?
困难
参考答案
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