Python 面试系列:常见 100 个经典面试问题,从入门到进阶
说明:本文整理了 Python 面试中常见的 100 个经典问题,覆盖基础语法、数据结构、函数、装饰器、异常、文件、迭代器、生成器、面向对象、工程化、并发、标准库、性能优化等内容。每个问题都尽量配有示例代码和真实输出,方便读者边看边练。
前言
Python 面试并不是单纯考"会不会写语法",而是考你是否真正理解对象、数据结构、函数、异常、面向对象、装饰器、生成器、并发、性能、工程化和常见坑点。
很多候选人能写出代码,但一问:
- 为什么列表不能作为字典 key?
- 装饰器什么时候执行?
is和==有什么区别?- 线程、进程、协程怎么选?
- GIL 到底影响什么?
- 浅拷贝和深拷贝有什么区别?
就容易卡住。
本文整理 100 个 Python 高频面试问题,每个问题都配一个小例子,方便你边看边练。建议阅读方式是:先看问题,自己想 30 秒,再看答案和代码。
一、Python 基础语法与对象模型
1. Python 是解释型语言吗?
是。Python 通常由解释器执行源码或字节码,不需要像 C/C++ 那样提前编译成机器码。
但这不代表 Python 没有编译过程。.py 文件通常会先被编译成字节码,再由 Python 虚拟机执行。
python
print("hello python")
输出:
text
hello python
2. Python 中"一切皆对象"是什么意思?
变量、函数、类、模块、数字、字符串都是对象。对象通常有三个重要特征:
- 身份:可以用
id()查看 - 类型:可以用
type()查看 - 值:对象保存的数据
python
x = 100
print(type(x))
print(id(x))
输出示例:
text
<class 'int'>
140735568356104
3. is 和 == 有什么区别?
== 比较的是两个对象的值是否相等。
is 比较的是两个变量是否引用同一个对象。
python
a = [1, 2]
b = [1, 2]
print(a == b)
print(a is b)
输出:
text
True
False
解释:
虽然 a 和 b 的值一样,但它们是两个不同的列表对象。
4. Python 中可变对象和不可变对象有什么区别?
可变对象的值可以原地修改,例如:
listdictset
不可变对象创建后不能直接修改,例如:
intfloatstrtuple
python
a = [1, 2]
a.append(3)
print(a)
s = "ab"
s += "c"
print(s)
输出:
text
[1, 2, 3]
abc
注意:字符串看起来被修改了,其实是创建了一个新的字符串对象。
5. 为什么列表不能作为字典 key?
因为列表是可变对象,哈希值不稳定,不能作为字典 key。
字典 key 必须是可哈希对象。常见可哈希对象包括:
- 字符串
- 数字
- 元组
- 布尔值
python
d = {}
# d[[1, 2]] = "value" # TypeError: unhashable type: 'list'
d[(1, 2)] = "value"
print(d)
输出:
text
{(1, 2): 'value'}
6. None 应该如何判断?
推荐使用 is None,不要使用 == None。
python
value = None
if value is None:
print("value is None")
输出:
text
value is None
原因是 None 是一个单例对象,使用 is 更准确。
7. Python 的布尔值判断规则是什么?
以下值通常会被判断为假:
NoneFalse0""[]{}set()tuple()
python
values = ["", [], {}, 0, None, "python"]
for v in values:
print(bool(v))
输出:
text
False
False
False
False
False
True
8. and 和 or 返回的一定是布尔值吗?
不一定。它们返回的是参与运算的对象本身。
python
print(0 or "default")
print("python" and "ok")
输出:
text
default
ok
这个特性经常用于默认值处理。
9. Python 中变量需要声明类型吗?
不需要。Python 是动态类型语言,变量名只是对象的引用。
python
x = 1
x = "hello"
print(x)
输出:
text
hello
10. Python 的类型注解会强制校验类型吗?
不会。类型注解主要用于:
- 提高代码可读性
- 辅助 IDE 提示
- 配合 mypy、pyright 等工具做静态检查
Python 运行时默认不会强制校验类型。
python
def add(a: int, b: int) -> int:
return a + b
print(add("1", "2"))
输出:
text
12
虽然参数标注为 int,但运行时传入字符串也能执行。
二、字符串、列表、元组、字典、集合
11. 字符串如何反转?
python
s = "python"
print(s[::-1])
输出:
text
nohtyp
12. 字符串拼接为什么推荐 join?
循环中频繁使用 + 拼接字符串,会产生多个中间字符串对象。
大量字符串拼接时,推荐使用 join()。
python
words = ["Python", "is", "good"]
print(" ".join(words))
输出:
text
Python is good
13. split() 和 strip() 有什么区别?
split() 用于分割字符串。
strip() 用于去除字符串首尾空白或指定字符。
python
s = " a,b,c "
print(s.strip())
print(s.strip().split(","))
输出:
text
a,b,c
['a', 'b', 'c']
14. 列表推导式是什么?
列表推导式可以用简洁语法生成列表。
python
nums = [x * x for x in range(5)]
print(nums)
输出:
text
[0, 1, 4, 9, 16]
15. 列表推导式中如何加条件?
python
nums = [x for x in range(10) if x % 2 == 0]
print(nums)
输出:
text
[0, 2, 4, 6, 8]
16. append() 和 extend() 有什么区别?
append() 添加一个元素。
extend() 展开可迭代对象后逐个添加。
python
a = [1, 2]
a.append([3, 4])
print(a)
b = [1, 2]
b.extend([3, 4])
print(b)
输出:
text
[1, 2, [3, 4]]
[1, 2, 3, 4]
17. list.sort() 和 sorted() 有什么区别?
list.sort() 是原地排序,返回 None。
sorted() 会返回一个新的列表。
python
a = [3, 1, 2]
b = sorted(a)
print(a)
print(b)
a.sort()
print(a)
输出:
text
[3, 1, 2]
[1, 2, 3]
[1, 2, 3]
18. 元组一定不可变吗?
元组本身不可变,但如果元组中包含可变对象,可变对象内部仍然可以修改。
python
t = ([1, 2], "a")
t[0].append(3)
print(t)
输出:
text
([1, 2, 3], 'a')
19. 字典如何安全获取不存在的 key?
使用 get()。
python
user = {"name": "vito"}
print(user.get("age"))
print(user.get("age", 18))
输出:
text
None
18
20. 字典如何遍历 key 和 value?
python
user = {"name": "vito", "age": 18}
for k, v in user.items():
print(k, v)
输出:
text
name vito
age 18
21. 字典推导式是什么?
python
d = {x: x * x for x in range(3)}
print(d)
输出:
text
{0: 0, 1: 1, 2: 4}
22. 集合有什么特点?
集合的特点:
- 元素唯一
- 无序
- 适合去重
- 适合成员判断
python
nums = [1, 2, 2, 3]
print(set(nums))
输出:
text
{1, 2, 3}
23. 集合常见运算有哪些?
python
a = {1, 2, 3}
b = {3, 4}
print(a | b)
print(a & b)
print(a - b)
输出:
text
{1, 2, 3, 4}
{3}
{1, 2}
24. 如何统计列表中元素出现次数?
python
from collections import Counter
items = ["a", "b", "a", "c", "b", "a"]
print(Counter(items))
输出:
text
Counter({'a': 3, 'b': 2, 'c': 1})
25. 如何取列表中出现最多的元素?
python
from collections import Counter
items = ["a", "b", "a", "c", "b", "a"]
print(Counter(items).most_common(1))
输出:
text
[('a', 3)]
三、函数与参数
26. Python 函数参数有哪些类型?
常见参数类型包括:
- 位置参数
- 默认参数
- 关键字参数
- 可变位置参数
*args - 可变关键字参数
**kwargs
python
def demo(a, b=2, *args, **kwargs):
print(a, b, args, kwargs)
demo(1, 3, 4, 5, name="vito")
输出:
text
1 3 (4, 5) {'name': 'vito'}
27. 默认参数有什么坑?
默认参数只在函数定义时创建一次。
如果默认参数是列表、字典等可变对象,可能导致数据被多次调用共享。
python
def add_item(item, box=[]):
box.append(item)
return box
print(add_item("a"))
print(add_item("b"))
输出:
text
['a']
['a', 'b']
推荐写法:
python
def add_item(item, box=None):
if box is None:
box = []
box.append(item)
return box
print(add_item("a"))
print(add_item("b"))
输出:
text
['a']
['b']
28. *args 是什么?
*args 用于接收多余的位置参数,类型是元组。
python
def total(*args):
return sum(args)
print(total(1, 2, 3))
输出:
text
6
29. **kwargs 是什么?
**kwargs 用于接收多余的关键字参数,类型是字典。
python
def show(**kwargs):
print(kwargs)
show(name="vito", age=18)
输出:
text
{'name': 'vito', 'age': 18}
30. Python 函数是对象吗?
是。函数可以赋值给变量,也可以作为参数传递。
python
def hello():
return "hello"
f = hello
print(f())
输出:
text
hello
31. 什么是高阶函数?
接收函数作为参数,或者返回函数的函数,称为高阶函数。
python
def apply(func, value):
return func(value)
print(apply(abs, -10))
输出:
text
10
32. lambda 表达式是什么?
lambda 是匿名函数,适合简单逻辑。
python
add = lambda a, b: a + b
print(add(1, 2))
输出:
text
3
实际项目中,不建议写过于复杂的 lambda。
33. map() 怎么用?
map() 会对可迭代对象中的每个元素应用函数。
python
nums = [1, 2, 3]
print(list(map(lambda x: x * 2, nums)))
输出:
text
[2, 4, 6]
34. filter() 怎么用?
filter() 用于过滤元素。
python
nums = [1, 2, 3, 4]
print(list(filter(lambda x: x % 2 == 0, nums)))
输出:
text
[2, 4]
35. zip() 怎么用?
zip() 会把多个可迭代对象按位置打包。
python
names = ["a", "b"]
scores = [90, 80]
print(list(zip(names, scores)))
输出:
text
[('a', 90), ('b', 80)]
四、作用域、闭包、装饰器
36. Python 作用域查找规则是什么?
Python 变量查找遵循 LEGB 规则:
- Local:局部作用域
- Enclosing:外层函数作用域
- Global:全局作用域
- Built-in:内置作用域
python
x = "global"
def outer():
x = "outer"
def inner():
x = "inner"
print(x)
inner()
outer()
输出:
text
inner
37. global 有什么作用?
global 用于在函数内部声明并修改全局变量。
python
count = 0
def inc():
global count
count += 1
inc()
print(count)
输出:
text
1
38. nonlocal 有什么作用?
nonlocal 用于修改外层函数作用域中的变量。
python
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
f = outer()
print(f())
print(f())
输出:
text
1
2
39. 什么是闭包?
函数内部定义函数,并且内部函数引用了外部函数的变量,这种结构就是闭包。
python
def make_multiplier(n):
def mul(x):
return x * n
return mul
double = make_multiplier(2)
print(double(5))
输出:
text
10
40. 什么是装饰器?
装饰器本质上是一个函数,用于在不修改原函数代码的情况下增强功能。
python
def log(func):
def wrapper():
print("before")
func()
print("after")
return wrapper
@log
def hello():
print("hello")
hello()
输出:
text
before
hello
after
41. 装饰器如何保留原函数信息?
使用 functools.wraps。
python
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@log
def hello():
"""say hello"""
print("hello")
print(hello.__name__)
print(hello.__doc__)
输出:
text
hello
say hello
42. 带参数的装饰器怎么写?
python
from functools import wraps
def repeat(n):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(2)
def say():
print("hi")
say()
输出:
text
hi
hi
五、异常处理
43. Python 如何捕获异常?
python
try:
x = 1 / 0
except ZeroDivisionError as e:
print("error:", e)
输出:
text
error: division by zero
44. try...except...else...finally 执行顺序是什么?
else 在没有异常时执行。
finally 无论是否异常都会执行。
python
try:
x = 10 / 2
except ZeroDivisionError:
print("except")
else:
print("else:", x)
finally:
print("finally")
输出:
text
else: 5.0
finally
45. 如何主动抛出异常?
使用 raise。
python
def check_age(age):
if age < 0:
raise ValueError("age cannot be negative")
return age
try:
check_age(-1)
except ValueError as e:
print(e)
输出:
text
age cannot be negative
46. 如何自定义异常?
python
class BusinessError(Exception):
pass
raise BusinessError("业务异常")
输出示例:
text
BusinessError: 业务异常
47. 为什么不要直接 except Exception: pass?
因为这样会吞掉错误,导致问题隐藏,排查困难。
python
try:
int("abc")
except Exception:
print("发生异常,但这里至少要记录日志")
输出:
text
发生异常,但这里至少要记录日志
六、文件、上下文管理器、迭代器、生成器
48. 为什么推荐使用 with open()?
with 会自动关闭文件,避免资源泄漏。
python
with open("demo.txt", "w", encoding="utf-8") as f:
f.write("hello")
49. 什么是上下文管理器?
实现 __enter__ 和 __exit__ 方法的对象,就是上下文管理器。
python
class Demo:
def __enter__(self):
print("enter")
return self
def __exit__(self, exc_type, exc, tb):
print("exit")
with Demo():
print("running")
输出:
text
enter
running
exit
50. 什么是迭代器?
实现 __iter__() 和 __next__() 的对象就是迭代器。
python
nums = iter([1, 2, 3])
print(next(nums))
print(next(nums))
输出:
text
1
2
51. 什么是可迭代对象?
能被 for 循环遍历的对象,就是可迭代对象。
常见可迭代对象包括:
- 列表
- 元组
- 字典
- 字符串
- 集合
- 生成器
python
for ch in "abc":
print(ch)
输出:
text
a
b
c
52. 什么是生成器?
包含 yield 的函数会返回生成器。生成器会按需产生数据。
python
def gen():
yield 1
yield 2
g = gen()
print(next(g))
print(next(g))
输出:
text
1
2
53. 生成器有什么优点?
生成器节省内存,适合处理大数据流。
python
def numbers(n):
for i in range(n):
yield i
for x in numbers(3):
print(x)
输出:
text
0
1
2
54. yield from 有什么作用?
yield from 可以把子生成器的值交给外层生成器。
python
def gen():
yield from [1, 2, 3]
print(list(gen()))
输出:
text
[1, 2, 3]
55. 生成器表达式和列表推导式有什么区别?
生成器表达式按需计算。
列表推导式会一次性生成完整列表。
python
g = (x * x for x in range(3))
print(g)
print(list(g))
输出示例:
text
<generator object <genexpr> at 0x...>
[0, 1, 4]
七、面向对象
56. 类和对象有什么区别?
类是模板,对象是类的实例。
python
class User:
pass
u = User()
print(type(u))
输出:
text
<class '__main__.User'>
57. __init__ 是构造函数吗?
严格来说,__init__ 是初始化方法,对象创建后会调用它。
真正创建对象的是 __new__。
python
class User:
def __init__(self, name):
self.name = name
u = User("vito")
print(u.name)
输出:
text
vito
58. 实例属性和类属性有什么区别?
实例属性属于对象。
类属性属于类。
python
class User:
role = "tester"
u1 = User()
u2 = User()
u1.name = "vito"
print(u1.role)
print(u2.role)
print(u1.name)
输出:
text
tester
tester
vito
59. 类方法、静态方法、实例方法有什么区别?
实例方法默认接收 self。
类方法默认接收 cls。
静态方法不自动接收对象或类。
python
class Demo:
def ins(self):
print("instance")
@classmethod
def cls_method(cls):
print("class")
@staticmethod
def static_method():
print("static")
d = Demo()
d.ins()
Demo.cls_method()
Demo.static_method()
输出:
text
instance
class
static
60. 什么是继承?
子类可以复用父类的属性和方法。
python
class Animal:
def speak(self):
print("animal speak")
class Dog(Animal):
pass
Dog().speak()
输出:
text
animal speak
61. 什么是方法重写?
子类重新定义父类方法,就是方法重写。
python
class Animal:
def speak(self):
print("animal")
class Dog(Animal):
def speak(self):
print("wang")
Dog().speak()
输出:
text
wang
62. super() 有什么作用?
super() 用于调用父类方法。
python
class Base:
def hello(self):
print("base hello")
class Child(Base):
def hello(self):
super().hello()
print("child hello")
Child().hello()
输出:
text
base hello
child hello
63. Python 支持多继承吗?
支持。方法解析顺序由 MRO 决定。
python
class A:
def hello(self):
print("A")
class B(A):
pass
class C(A):
def hello(self):
print("C")
class D(B, C):
pass
D().hello()
print(D.mro())
输出示例:
text
C
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
64. 什么是魔术方法?
以双下划线开头和结尾的方法,例如:
__str____repr____len____call____enter____exit__
python
class User:
def __str__(self):
return "User object"
print(User())
输出:
text
User object
65. __repr__ 和 __str__ 有什么区别?
__str__ 面向用户,输出更友好。
__repr__ 面向开发者和调试。
python
class User:
def __repr__(self):
return "User(name='vito')"
u = User()
print(repr(u))
输出:
text
User(name='vito')
66. @property 有什么作用?
@property 可以把方法当属性访问,同时封装计算逻辑。
python
class Circle:
def __init__(self, r):
self.r = r
@property
def area(self):
return 3.14 * self.r * self.r
c = Circle(2)
print(c.area)
输出:
text
12.56
67. 什么是 dataclass?
dataclass 可以自动生成 __init__、__repr__ 等方法,适合定义数据类。
python
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
u = User("vito", 18)
print(u)
输出:
text
User(name='vito', age=18)
68. __slots__ 有什么作用?
__slots__ 可以限制实例动态添加属性,也可以减少内存开销。
python
class User:
__slots__ = ("name",)
u = User()
u.name = "vito"
print(u.name)
# u.age = 18 # AttributeError
输出:
text
vito
八、模块、包、虚拟环境、工程化
69. 模块和包有什么区别?
一个 .py 文件可以是模块。
包含多个模块的目录可以作为包。
python
# demo.py
def hello():
return "hello"
使用:
python
# import demo
# print(demo.hello())
70. if __name__ == "__main__" 有什么作用?
判断当前文件是被直接运行,还是被其他模块导入。
python
def main():
print("running main")
if __name__ == "__main__":
main()
直接运行输出:
text
running main
71. Python 如何管理第三方依赖?
常用 pip 安装依赖,并推荐使用虚拟环境。
bash
python -m venv .venv
source .venv/bin/activate
pip install requests
Windows PowerShell:
powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install requests
72. requirements.txt 有什么用?
requirements.txt 用于记录项目依赖,方便环境复现。
text
requests==2.32.3
pytest==8.3.2
安装依赖:
bash
pip install -r requirements.txt
73. pyproject.toml 是什么?
pyproject.toml 是现代 Python 项目常见配置文件,可配置:
- 项目信息
- 构建系统
- pytest 配置
- black 配置
- mypy 配置
- ruff 配置
toml
[project]
name = "demo"
version = "0.1.0"
[tool.pytest.ini_options]
testpaths = ["tests"]
74. Python 如何读取环境变量?
python
import os
os.environ["APP_ENV"] = "dev"
print(os.getenv("APP_ENV"))
输出:
text
dev
75. __all__ 有什么作用?
__all__ 用于控制 from module import * 导入的内容。
python
__all__ = ["hello"]
def hello():
print("hello")
def hidden():
print("hidden")
九、并发、线程、进程、协程
76. 线程适合什么场景?
线程适合 IO 密集型任务,例如:
- 网络请求
- 文件读写
- 数据库访问
- 接口调用
python
import threading
import time
def task(name):
time.sleep(1)
print(name, "done")
t1 = threading.Thread(target=task, args=("A",))
t2 = threading.Thread(target=task, args=("B",))
t1.start()
t2.start()
t1.join()
t2.join()
输出:
text
A done
B done
77. 什么是 GIL?
GIL 是 CPython 中的全局解释器锁。
它会限制多个线程同时执行 Python 字节码,所以 CPU 密集型任务通常无法通过多线程获得明显性能提升。
python
def cpu_task():
return sum(i * i for i in range(100000))
print(cpu_task())
输出:
text
333328333350000
78. 进程适合什么场景?
进程适合 CPU 密集型任务。
多进程可以利用多核 CPU。
python
from multiprocessing import Pool
def square(x):
return x * x
if __name__ == "__main__":
with Pool(2) as pool:
print(pool.map(square, [1, 2, 3]))
输出:
text
[1, 4, 9]
79. 为什么 Windows 下多进程要写 if __name__ == "__main__"?
因为 Windows 创建子进程时会重新导入主模块。
如果不加保护,可能导致子进程重复创建子进程。
python
from multiprocessing import Process
def hello():
print("hello")
if __name__ == "__main__":
p = Process(target=hello)
p.start()
p.join()
输出:
text
hello
80. 协程适合什么场景?
协程适合高并发 IO 场景,例如:
- 网络请求
- WebSocket
- 异步数据库
- 异步爬虫
- 高并发接口调用
python
import asyncio
async def main():
print("start")
await asyncio.sleep(1)
print("end")
asyncio.run(main())
输出:
text
start
end
81. async 和 await 是什么?
async 用于定义协程函数。
await 用于等待异步操作完成。
python
import asyncio
async def fetch():
await asyncio.sleep(1)
return "data"
async def main():
result = await fetch()
print(result)
asyncio.run(main())
输出:
text
data
82. 如何并发运行多个协程?
使用 asyncio.gather()。
python
import asyncio
async def task(name):
await asyncio.sleep(1)
return f"{name} done"
async def main():
result = await asyncio.gather(task("A"), task("B"))
print(result)
asyncio.run(main())
输出:
text
['A done', 'B done']
83. time.sleep() 和 asyncio.sleep() 有什么区别?
time.sleep() 会阻塞当前线程。
asyncio.sleep() 会让出事件循环,不阻塞其他协程。
python
import asyncio
async def task():
await asyncio.sleep(1)
print("done")
asyncio.run(task())
输出:
text
done
84. 如何避免多个线程同时修改共享数据?
可以使用锁。
python
import threading
count = 0
lock = threading.Lock()
def inc():
global count
for _ in range(10000):
with lock:
count += 1
threads = [threading.Thread(target=inc) for _ in range(2)]
for t in threads:
t.start()
for t in threads:
t.join()
print(count)
输出:
text
20000
十、常用标准库与实战能力
85. collections.defaultdict 有什么用?
defaultdict 可以给不存在的 key 自动创建默认值。
python
from collections import defaultdict
d = defaultdict(list)
d["python"].append("good")
print(d)
输出:
text
defaultdict(<class 'list'>, {'python': ['good']})
86. deque 适合什么场景?
deque 是双端队列,适合从两端快速添加和删除元素。
python
from collections import deque
q = deque([1, 2])
q.appendleft(0)
q.append(3)
print(q)
输出:
text
deque([0, 1, 2, 3])
87. heapq 有什么用?
heapq 可以实现堆,常用于:
- Top K
- 优先队列
- 小顶堆
python
import heapq
nums = [5, 1, 3]
heapq.heapify(nums)
print(heapq.heappop(nums))
输出:
text
1
88. json.dumps() 和 json.loads() 有什么区别?
json.dumps() 把 Python 对象转成 JSON 字符串。
json.loads() 把 JSON 字符串转成 Python 对象。
python
import json
s = json.dumps({"name": "vito"}, ensure_ascii=False)
print(s)
obj = json.loads(s)
print(obj["name"])
输出:
text
{"name": "vito"}
vito
89. 如何处理 Decimal 不能直接 JSON 序列化的问题?
可以转换为字符串或浮点数。
如果是金额、精度敏感场景,建议转换为字符串。
python
import json
from decimal import Decimal
data = {"price": str(Decimal("10.50"))}
print(json.dumps(data))
输出:
text
{"price": "10.50"}
90. pathlib 相比 os.path 有什么好处?
pathlib 是面向对象的路径处理方式,路径拼接更直观。
python
from pathlib import Path
p = Path("logs") / "app.log"
print(p)
输出:
text
logs/app.log
91. 如何读取 CSV 文件?
python
import csv
rows = [["name", "score"], ["vito", "90"]]
with open("score.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(rows)
with open("score.csv", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
print(row)
输出:
text
{'name': 'vito', 'score': '90'}
92. 如何记录日志?
python
import logging
logging.basicConfig(level=logging.INFO)
logging.info("service started")
输出示例:
text
INFO:root:service started
93. 如何写单元测试?
python
def add(a, b):
return a + b
def test_add():
assert add(1, 2) == 3
使用 pytest 运行:
bash
pytest test_demo.py
输出示例:
text
1 passed in 0.01s
94. pytest.raises 怎么用?
python
import pytest
def div(a, b):
return a / b
def test_div_zero():
with pytest.raises(ZeroDivisionError):
div(1, 0)
输出示例:
text
1 passed in 0.01s
十一、性能、内存、编码规范
95. 如何查看对象占用内存?
python
import sys
a = [1, 2, 3]
print(sys.getsizeof(a))
输出示例:
text
88
具体数值和 Python 版本、操作系统有关。
96. 如何优化大列表处理?
能用生成器就不要一次性创建大列表。
python
total = sum(x * x for x in range(1000000))
print(total)
输出:
text
333332833333500000
97. 浅拷贝和深拷贝有什么区别?
浅拷贝只复制外层对象。
深拷贝会递归复制内部对象。
python
import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a)
c = copy.deepcopy(a)
a[0].append(99)
print(b)
print(c)
输出:
text
[[1, 2, 99], [3, 4]]
[[1, 2], [3, 4]]
98. with 为什么比手动 close() 更安全?
因为即使中间发生异常,with 也会执行资源释放逻辑。
python
try:
with open("demo.txt", "r", encoding="utf-8") as f:
print(f.read())
except FileNotFoundError:
print("file not found")
输出示例:
text
hello
或者:
text
file not found
99. Python 代码规范主要看什么?
常见关注点包括:
- 命名清晰
- 函数不要太长
- 异常不要乱吞
- 不要写重复代码
- 复杂逻辑拆函数
- 遵循 PEP 8 风格
- 加必要的类型注解
- 加必要的单元测试
python
def calculate_total_price(price: float, count: int) -> float:
return price * count
print(calculate_total_price(9.9, 3))
输出:
text
29.700000000000003
如果涉及金额,建议使用 Decimal。
100. 面试中如何回答"你怎么提升 Python 代码质量"?
可以从以下几个方面回答:
- 使用类型注解提升可读性
- 使用 pytest 编写单元测试
- 使用 logging 记录关键日志
- 使用异常处理保证程序稳定
- 使用虚拟环境隔离依赖
- 使用 requirements.txt 或 pyproject.toml 管理依赖
- 使用 black、ruff、mypy 等工具做代码检查
- 使用 CI 流水线自动执行测试和代码扫描
- 复杂逻辑模块化
- 性能敏感代码做 profiling 分析
python
from typing import Sequence
def average(nums: Sequence[float]) -> float:
if not nums:
raise ValueError("nums cannot be empty")
return sum(nums) / len(nums)
print(average([80, 90, 100]))
输出:
text
90.0
十二、总结:Python 面试不是背答案,而是建立知识网络
这 100 个问题可以分成 8 条主线:
- 基础语法:变量、对象、类型、真假判断
- 数据结构:字符串、列表、元组、字典、集合
- 函数能力:参数、闭包、装饰器、高阶函数
- 面向对象:类、继承、魔术方法、属性管理
- 异常与资源:异常捕获、上下文管理器、文件处理
- 并发模型:线程、进程、协程、GIL、锁
- 标准库:json、csv、pathlib、logging、collections
- 工程质量:虚拟环境、依赖管理、测试、类型注解、代码规范
真正的面试重点不是"你是否见过这个问题",而是你能否用清晰的语言说明原理,并写出可靠的小例子。
最好的准备方式是:
- 每个问题都自己敲一遍代码
- 观察真实输出
- 尝试修改参数和边界条件
- 主动制造异常场景
- 把问题串成知识体系
当你能把一个知识点讲清楚、写明白、举出例子,并能说出常见坑点时,面试基本就稳了一半。
附:面试复习建议
如果你准备 Python 面试,可以按下面顺序复习:
第一阶段:基础语法
重点掌握:
- 变量
- 类型
- 判断
- 循环
- 字符串
- 列表
- 字典
- 集合
第二阶段:函数和对象
重点掌握:
- 参数传递
- 默认参数坑点
- 闭包
- 装饰器
- 类
- 继承
- 魔术方法
第三阶段:工程能力
重点掌握:
- 虚拟环境
- pip
- requirements.txt
- pyproject.toml
- 日志
- 异常
- 单元测试
第四阶段:进阶能力
重点掌握:
- 迭代器
- 生成器
- 上下文管理器
- 多线程
- 多进程
- 协程
- GIL
- 性能优化
第五阶段:项目表达
面试时不要只说"我会 Python",而要结合项目讲:
- 我用 Python 做过什么
- 解决了什么问题
- 遇到过什么坑
- 如何排查问题
- 如何提升效率
- 如何保证代码质量
例如:
我在自动化测试平台中使用 Python 编写接口测试脚本,通过 pytest 管理测试用例,结合 logging 输出关键日志,并使用 requests 封装接口调用。对于复杂业务场景,我会把公共逻辑抽成工具函数或类,同时通过 fixture 管理测试数据,提升了用例复用性和维护性。
这样的回答,比单纯背语法更有说服力。
参考方向
本文内容主要围绕 Python 常见面试点整理,建议进一步阅读:
- Python 官方文档
- Python Tutorial
- Python Data Model
- Python Standard Library
- pytest 官方文档
- Python Packaging 官方文档
- asyncio 官方文档
- threading 官方文档
- multiprocessing 官方文档