Python面试必刷50题:六大模块考点解析与底层原理

📅 发布时间:2026/10/11 7:25:32
Python面试必刷50题:六大模块考点解析与底层原理
最近私信里被问得最多的一个问题“Python面试到底该刷什么题”老实说每次看到这种问题我都容易犯难。市面上的题单要么是几百道的大杂烩要么只有题干没有解析真正能帮到人的不多。这次我把自己这些年作为面试官、作为被面者亲历过的题目做了几轮筛选整理出这份50道Python面试题清单覆盖基础语法、函数、面向对象、高级特性、并发和实战排查六个模块。每道题都写了考查点和答题要点建议先收藏再慢慢刷。这份题单的定位很明确不追偏题怪题只收面试里真正会出现、真正能区分水平的核心考点。准备实习和校招的同学能用想跳槽的社招选手能用哪怕你是刚转行学Python的读者把它当成一份主动查漏补缺的清单也比死记硬背有用得多。下面我按模块拆开讲每个模块都给了具体的刷法建议。1. 这50道题怎么刷才算真正吃透1.1 这份题单从哪来、适合谁先说我观察到的现象很多人刷题是“看题干—翻答案—复制—背过”过程看着很努力结果一到面试现场面试官换个问法就懵了。原因很简单你记住的是答案不是答案背后的原理。这50道题是我从一线面试场景里挑出来的不是从网上随便拼的。比如Python里is和的区别看起来是个送分题但面试官接着问一句“为什么小整数会被缓存”“为什么用判断浮点数会翻车”能答上来的人立刻少一多半。所以这份题单里每道题我都会把底层机制讲透而不是只给一句标准答案。适合的人群其实很广准备校招实习的、准备社招跳槽的、自学Python想检验水平的都可以拿它当对照清单。1.2 三遍刷题法和环境准备我建议用三遍刷法。第一遍按模块过题目看到题干先自己默想答案想不出来的做好标记但先不要看解析自己写个小demo验证第二遍只看标记过的题尝试用自己的话把原理讲出来能讲通才算过第三遍是面试前两三天把这份题单当抽问卡随机挑题目限时作答顺便练口头表达。刷题时强烈建议开一个Python解释器随手验证。很多题光靠脑子想容易想错比如负索引切片、浅拷贝与深拷贝、装饰器的执行顺序动手跑一遍记忆会深很多。环境没装的话去Python官网下载对应系统版本安装好再把pip配好后面写demo、跑小脚本都方便。编辑器用VS Code或者PyCharm都行重点是把Python解释器路径配对别一运行就报“python was not found”。2. 基础语法与数据模型面试第一关的必问题基础题不是送分题恰恰是刷人最多的环节。面试官习惯从最基础的问题切入用来观察你的表达是否严谨、对语言的理解是否停留在“会用”的层面。2.1 变量、引用与不可变对象第1~2题第1题is和的区别是什么比较的是值是否相等调用的是对象的__eq__方法is比较的是身份也就是两个引用是否指向内存中的同一个对象。看起来简单陷阱藏在细节里a [1, 2, 3] b [1, 2, 3] print(a b) # True print(a is b) # False但换成小整数就不一样了x 257 y 257 print(x is y)CPython对小整数一般是 -5 到 256会做缓存所以这个范围内的整数用is比较往往返回True。面试时主动把这个点讲出来比死记“is是身份比较、是值比较”要好得多。实际业务代码里判断None推荐用is None判断数值相等用这是规范问题也是隐藏的性能细节。第2题可变对象和不可变对象有哪些函数参数按什么传递可变对象是列表、字典、集合、bytearray不可变对象是整数、浮点数、字符串、元组、frozenset。“按值还是按引用”这个问法在Python里其实不够准确更严谨的说法是“传对象引用”但要注意侧重点不可变对象一旦修改就会生成新对象不影响原引用可变对象原地修改则会影响外部。def demo(lst): lst.append(1) return lst a [] demo(a) print(a) # [1]可变对象原地修改外部可见这道题答得好不好直接暴露你对Python对象模型的理解程度。在很多数据分析、量化交易脚本里函数对DataFrame或列表的原地修改都是从这里延伸出去的坑。2.2 列表、字典与切片的高频考法第3~5题第3题浅拷贝和深拷贝怎么区分浅拷贝只复制第一层嵌套的可变对象还是共享引用深拷贝递归复制所有层。用copy.copy和copy.deepcopy实现。实际工程里拷贝一个包含嵌套结构的配置字典时用浅拷贝改内层会出现“改一处全家变”的问题所以必须用深拷贝。面试经常顺带问一句“为什么对两个深拷贝的复杂对象仍然为True”答案涉及逐层值比较可以顺势引出__eq__的递归概念。第4题dict查找为什么快底层用的什么结构dict底层是哈希表插入时对key做哈希得到索引查找时同样哈希定位平均O(1)。需要记住key必须是可哈希的所以list不能做keytuple只要内部元素都可哈希就行。哈希冲突时Python用开放寻址法解决。集合set底层也是哈希表只是不存value。这里面试官常追问“为什么字典在Python 3.7之后保持插入顺序”答3.6版本只是CPython的实现细节3.7起已经成为语言规范。第5题列表切片a[::-1]为什么能反转切片是start:stop:step的缩写step为 -1 时会从后往前取值。a[::-1]等价于从最后一个元素开始以步长 -1 遍历到开头返回新列表。注意切片生成的是新对象不会原地修改原列表。延伸考点a[1:3]、a[:-1]、a[::2]分别是什么考的是对切片三参数的掌握。2.3 内置函数与推导式的实战细节第6~8题第6题sort和sorted有什么区别key参数怎么用list.sort()是原地排序返回Nonesorted()返回新列表可作用于任何可迭代对象。两者都支持key和reverse参数。key接收一个函数对每个元素调用后返回排序依据。经典场景对字典列表按某个字段排序。users [{name: Alice, age: 30}, {name: Bob, age: 25}] sorted(users, keylambda u: u[age])写排行榜、数据报表时这段代码几乎天天用。扩展一下还能配合operator.itemgetter比lambda性能更好这也是很多人不知道的细节。第7题列表推导式和生成器表达式的区别语法上列表推导式用[]生成器表达式用()。执行时列表推导式一次性生成整个列表占用内存生成器表达式惰性求值每次只生成一个元素适合大数据流。面试常考内存对一个很大的range做过滤用列表推导式可能直接内存爆掉用生成器表达式配合sum()、max()、for循环就不会。还有一个衍生题两者性能差距多大答案是取决于场景。需要反复访问时列表推导式更快只遍历一次时生成器更省内存。第8题None、False、0、、[]的布尔值是什么Python中bool(0)、bool(False)、bool(None)、bool()、bool([])、bool({})都是False其余多为True。真正值得深入的是“为什么写if x:而不是if x is not None:”——前者判断真值后者判断是否为None。业务里用错会有诡异bug比如x0时if x:不进入分支但很多时候0是合法值。这个坑实战里踩的概率很高面试官很爱问。2.4 字符串、合并与去重的边角考点第9~12题第9题字符串常用方法有哪些什么时候用join高频方法基本是split、strip、replace、upper/lower、startswith/endswith、format。重点在拼接循环里用拼接字符串性能差因为字符串不可变每次拼接都生成新对象。正确做法是收集到列表再用.join()。写Web接口、拼SQL条件、生成报表时这个意识非常重要你可以在回答里主动提“频繁拼接改用join”面试官会点头。第10题list(abc)和list(123)分别会发生什么list(abc)得到[a, b, c]因为字符串可迭代list(123)会报TypeError因为整数不可迭代。反过来.join([a, b, c])才能回到字符串直接str([a, b])得到的是带括号的字符串。这类题看起来基础实际处理数据组合时最容易出问题尤其是把元组、列表、字符串互相倒腾时。第11题合并多个字典有哪些方式冲突时谁覆盖谁Python 3.9以上用|运算符之前用{**a, **b}。默认是后者覆盖前者。如果业务要求“冲突时保留旧值”就得手动遍历。还有一个细节dict(a, **b)只在b的key全部是字符串时可用因为**展开要求关键字。写微服务接口拼接配置时这道题考得很频繁。第12题如何去掉列表里的重复元素同时保持顺序最优雅的方式是dict.fromkeys(items).keys()因为字典天然保持插入顺序且key唯一。但如果元素不可哈希比如列表就得先转成可哈希形式。面试官想听的就是你对“可哈希”的理解能主动说清楚“整数、字符串、元组可哈希列表不可哈希”就加分。3. 函数与作用域别只会写def函数是Python的“一等公民”平时写业务写得多但真正能把参数解析、作用域、闭包讲清楚的候选人不多。3.1 参数类型与默认值陷阱第13~14题第13题位置参数、关键字参数、默认参数、*args、**kwargs分别怎么用位置参数按顺序传入关键字参数用namevalue显式指定。默认参数放在位置参数后面调用时可以不传。*args收集多余的位置参数为元组**kwargs收集多余的关键字参数为字典。追问点是为什么*args用元组、**kwargs用字典本质是Python函数调用协议f(*args, **kwargs)的展开与收集规则。理解了这一点写装饰器时包装任意函数签名就不会卡壳。第14题为什么默认参数不能是可变对象这是Python面试的保留节目def add_item(item, lst[]): lst.append(item) return lst两次调用后lst会累积之前的值。原因默认参数在函数定义时只求值一次之后每次调用如果没传该参数复用的都是同一个列表对象。正确写法是def add_item(item, lstNone)内部再做lst lst if lst is not None else []。回答时最好补一句“这是设计选择不是bug。Python的函数定义是一次性对象创建过程可变默认参数字面量在定义时被求值并缓存。”这道题能直接区分候选人是否真正写过代码。3.2 闭包与装饰器第15~16题第15题什么是闭包什么时候会用到闭包是指内层函数引用外层函数的变量并且外层函数返回内层函数时内层函数连同它引用的自由变量一起被保留。典型场景装饰器、偏函数、延迟计算。经典例子def make_counter(): count 0 def counter(): nonlocal count count 1 return count return counter这里nonlocal是必须的不加会在内层函数新建局部变量导致UnboundLocalError或行为不符合预期。追问点闭包占内存吗只要闭包对象存活被引用的自由变量就不会被回收这既是特点也是内存泄漏点。第16题装饰器的原理是什么手写一个计时装饰器。装饰器是接收函数并返回新函数的可调用对象decorator语法糖的本质是func decorator(func)。手写计时装饰器是必考题import time def timer(func): def wrapper(*args, **kwargs): start time.perf_counter() result func(*args, **kwargs) print(f{func.__name__} took {time.perf_counter() - start:.4f}s) return result return wrapper追问点如何让装饰器保留原函数信息答案是用functools.wraps它会复制__name__、__doc__等元数据到包装函数上。很多初学者搜“Python定义函数”“装饰器怎么用”其实都能从这一道题延伸开去。3.3 作用域与内置高阶函数第17~19题第17题global和nonlocal的区别global声明变量是模块级全局变量nonlocal声明变量是最近一层外层函数的局部变量。面试官喜欢的坑函数内给全局变量赋值但不加global会直接报UnboundLocalError嵌套函数内修改外层变量不写nonlocal同样报错。这个坑在写状态机、回调函数时经常出现。第18题lambda的特点和局限lambda是单表达式匿名函数不能包含语句和赋值。它适合用在高阶函数里比如sorted的key、map的映射函数。局限也很明显可读性差、无法复用、调试困难所以行业规范建议“能用def说清楚就别用lambda”。衍生题lambda x: x * x和def square(x): return x * x的区别行为等价但后者有__name__等信息异常回溯也更友好。第19题map、filter、reduce怎么用和推导式相比怎么选map对序列逐项映射filter按条件筛选reduce做累积计算。但面试官真正想确认的是你知不知道用列表推导式替代它们更符合Python风格。比如list(map(func, iterable))用[func(x) for x in iterable]替代可读性更好。不过多序列并行映射或已经有可调用对象时map写起来更省事。核心评判标准是别人能不能一眼看懂你的意图。3.4 函数进阶注解、递归与迭代工具第20~22题第20题函数注解和文档字符串有什么区别文档字符串用...描述功能函数注解在参数后加: type返回值用- type只是元信息不强制类型检查。配合mypy或typing模块可以做静态检查这在大型项目里非常有用。面试加分点是主动说“注解不会影响运行时性能但大幅提升可维护性”。第21题递归深度有限制吗如何优化递归默认递归深度约1000层超出会抛RecursionError。可以用sys.setrecursionlimit()调大但真正的问题是重复计算和栈溢出。更优做法是改迭代、加缓存。比如斐波那契递归加lru_cache既保留递归可读性又避免重复计算这是面试里的经典优化路径。第22题enumerate和zip的典型使用场景enumerate在遍历时同时拿索引和元素比range(len(lst))优雅得多。zip并行遍历多个可迭代对象还能用来“解包”成多个列表。需要注意zip在Python 3返回迭代器且长度以最短的为准想以最长的为准要用zip_longest。数据处理和清洗脚本里这两兄弟几乎天天用。4. 面向对象与异常从会用到底层理解Python的面向对象面试题很少考理论概念更多是让你手写类、分析继承顺序、讲清魔术方法调用时机。4.1 类属性、类方法与继承的基础第23~24题第23题类属性和实例属性的查找顺序实例属性存在self上类属性存在类上。访问obj.attr时先查实例属性再查类属性再往上查基类。经典坑class A: data [] a A() a.data.append(1) print(A.data) # [1]因为列表是共享引用解决办法是不要在类里定义可变默认值或者赋值时做防御性拷贝。这道题答好基本能过面向对象第一关。第24题实例方法、类方法、静态方法有什么区别实例方法第一个参数是self绑定实例类方法用classmethod第一个参数是cls绑定类静态方法用staticmethod不绑定任何东西相当于放在类里的普通函数。选择规则操作实例数据用实例方法需要访问类状态或当备选构造函数用类方法和类相关但不需要访问实例和类状态用静态方法。常考类方法实现备选构造class Date: def __init__(self, year, month, day): ... classmethod def from_string(cls, s): y, m, d map(int, s.split(-)) return cls(y, m, d)用Date.from_string(2024-05-01)创建实例读取外部数据文件或Excel里的日期字符串时特别好用。4.2 MRO、魔术方法与属性控制第25~27题第25题super()在多重继承里怎么工作super()不是简单“调用父类”而是按MRO方法解析顺序返回下一个类。多继承时MRO由C3线性化算法确定遵循单调性。要证明你真的懂可以写出class A: pass class B(A): pass class C(A): pass class D(B, C): pass print(D.__mro__)并能解释输出顺序。实际工程里多继承容易踩MRO相关的坑所以写代码尽量用组合代替多继承。追问“钻石继承会报错吗”答同名方法按MRO决定先后不会报错但语义可能和你预期不一样。第26题__new__和__init__的区别__str__和__repr__呢__new__是类方法负责创建实例在__init__之前执行__init__负责初始化。不可变类型的子类如果自定义行为比如实现一个不可变的自定义结构必须用__new__。__str__面向用户__repr__面向开发者__repr__没有覆盖时会退回object的默认实现。好的__repr__应该尽量能重建对象这在日志系统里非常加分排查问题时体验差距很明显。第27题property的作用如何把属性变成只读property把方法变成属性访问obj.x不用加括号。配合setter和deleter可以控制赋值与删除逻辑。典型实现class Circle: def __init__(self, radius): self._radius radius property def area(self): return 3.14159 * self._radius ** 2这里area就是只读的。追问“为什么用_radius”答这是约定俗成的私有标记。Python没有真正的私有__name只是名字改写外部仍能用_ClassName__name访问所以私有更多是约定而非强制。4.3 异常处理的最佳实践第28~30题第28题try/except/else/finally的执行顺序try里发生异常时跳到匹配的except没有异常时执行else无论是否异常finally都会执行。注意finally里如果有return会覆盖之前的return这是隐藏bug。实际项目中finally适合放关闭连接、释放锁这类确定性操作。第29题如何避免吞掉异常最常见反面教材是except Exception: pass异常被吞后线上问题根本定位不了。正确做法捕获能处理的异常类型处理不了就重新raise或者保留原始traceback再抛自定义异常。Python 3的raise ... from ...可以显式保留异常上下文面试官很喜欢深挖这一层看你对异常链是否真正理解。第30题自定义异常应该继承什么通常继承Exception不要直接继承BaseException因为KeyboardInterrupt、SystemExit这类系统级异常不该被业务代码随意捕获。自定义异常可以加字段记录上下文。实际项目里我会为每个模块定义异常基类再派生具体异常方便日志按模块过滤、按类型告警这是工程规范问题。4.4 鸭子类型与单例模式第31~32题第31题什么是鸭子类型“如果它走起来像鸭子、叫起来像鸭子那它就可以被当作鸭子。”Python不强制类型只要对象具备所需方法就能参与运算。静态类型语言里需要接口约束的场景Python里只要“看起来像”就行。面试加分做法举一个例子说明比如自定义一个类只要实现了read()方法就能被很多文件处理函数接受而不必继承某个基类。第32题单例模式怎么实现经典答案是重写__new__class Singleton: _instance None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance super().__new__(cls) return cls._instance也可以使用模块级全局变量因为模块天然只初始化一次这是Python里最简单的单例。面试追问“线程安全的单例怎么写”答加锁或用functools.lru_cache装饰工厂函数。这个问题能综合考察你对类机制并发的理解。5. 高级特性与并发拉开差距的硬核题这部分才是真正拉开差距的地方。很多人概念背得熟一写生成器和协程就露馅所以面试官通常会把压轴题放在这里。5.1 迭代器、生成器与协程第33~35题第33题可迭代对象、迭代器、生成器三者什么关系可迭代对象实现了__iter__迭代器实现了__iter__和__next__生成器是带yield的函数或生成器表达式它天然是迭代器。核心考点是惰性求值生成器每次next()才执行到下一个yield所以能处理无限序列、大文件分段读取。手写一个逐行读大文件的生成器def read_lines(path): with open(path, r, encodingutf-8) as f: for line in f: yield line比一次性readlines()省内存得多这也是面试官最想听到的实战结合点。第34题yield和return在生成器里怎么共存生成器函数里碰到return会触发StopIteration返回值会放进StopIteration.valuePython 3.3。普通for循环拿不到这个返回值但可以手动迭代或通过yield from传递。主动提到yield from在子生成器之间做委托会显得非常有水平。第35题async/await和生成器、协程的关系asyncio里的协程本质上是基于生成器协议发展起来的但async def定义的是原生协程跟生成器不再是同一类型。await用来挂起协程等待可等待对象。追问点什么时候用线程什么时候用协程答案I/O密集型且高延迟场景用asyncio比如大量HTTP请求的爬虫CPU密集型只能用多进程协程帮不上忙。这个话题在“Python协程”“Python爬虫”等搜索词里出现频率很高基本是为实战场景准备的。5.2 GIL、多线程与多进程第36~38题第36题什么是GIL它有什么影响GIL是CPython解释器里的全局解释器锁保证同一时刻只有一个线程执行字节码。它让CPU密集型多线程无法利用多核但I/O密集型多线程依然有效因为阻塞时会释放GIL。面试追问“既然GIL限制多线程那为什么还要学线程”答I/O密集场景下线程开发成本低共享状态简单CPU密集用多进程但进程间通信成本高。第37题多线程和多进程怎么选多线程共享进程内存需要锁保护多进程有独立内存用Queue、Pipe、共享内存等方式通信。进程开销大、启动慢、IPC慢但能突破GIL做多核并行。经典面试题“计算密集型任务12核机器用12个线程性能能提升12倍吗”答不能GIL下几乎只有1倍。改用ProcessPoolExecutor才能接近线性加速。如果你的任务是IO密集型比如批量读文件、拉接口线程池就够且更轻量。第38题死锁是怎么产生的如何避免死锁的四个条件互斥、持有并等待、不可剥夺、循环等待。Python常见死锁是多个锁获取顺序不一致比如线程A持有锁1去拿锁2线程B持有锁2去拿锁1。避免方法统一锁获取顺序、用with语句配合threading.Lock保证释放、加超时获取锁、能用queue消息队列就不要直接操作共享状态。现代Python代码里asyncio场景还要小心“事件循环中阻塞调用”这种另类死锁。5.3 上下文管理器和并发工具第39~42题第39题with语句的原理手写上下文管理器。with进入时调用__enter__退出时调用__exit__即使发生异常也会调用。两种手写方式实现魔术方法的类或者用contextlib.contextmanager配合生成器from contextlib import contextmanager contextmanager def log_ctx(): print(enter) try: yield finally: print(exit)yield之前对应__enter__之后对应__exit__。追问点__exit__接收哪几个参数答异常类型、异常实例、traceback如果方法返回True会抑制异常。第40题threading.local是干什么用的它给每个线程维护独立的变量副本避免全局变量在多线程里串数据。Web框架里请求级上下文经常用threading.local或协程的contextvars。这个点比较进阶答出来容易加分。第41题线程池和进程池怎么用标准库提供ThreadPoolExecutor和ProcessPoolExecutor接口一致都用submit()或map()提交任务。面试常考任务数远大于池容量时用as_completed()按完成顺序取结果避免阻塞在某个慢任务上。要注意ProcessPoolExecutor的任务函数必须可被序列化pickle所以不要在函数里塞lambda或局部闭包否则直接报错。这个细节不踩一次真的记不住。第42题asyncio场景下的超时和取消怎么处理用asyncio.wait_for(coro, timeout)控制协程超时超时会抛出asyncio.TimeoutError。取消任务用task.cancel()但要注意在finally里做资源清理。回答时如果能说“所有网络调用都应该加超时控制”面试官会刮目相看。很多线上偶发卡住的问题根源就是HTTP请求没设超时。6. 综合实战与排查题面试官最后的三板斧基础题答完面试官通常会丢几道手写题或场景排查题考察工程能力。这类题没有唯一答案但有一套清晰的解题路径往往是判断信息差的关键。6.1 高频手写代码题的破题思路第43~47题第43题手写斐波那契数列要求尽量优雅。最低要求是递归但直接递归指数级膨胀面试官会引导你优化到缓存或迭代。先写带缓存版本from functools import lru_cache lru_cache(maxsizeNone) def fib(n): if n 2: return n return fib(n - 1) fib(n - 2)再写迭代版讨论时间空间复杂度基本能过关。如果能主动说“用生成器实现无限斐波那契流”印象分会很高。第44题实现固定容量的去重保持插入顺序。用集合加列表辅助def dedup(items): seen set() result [] for item in items: if item not in seen: seen.add(item) result.append(item) return result注意元素不可哈希时的处理比如列表要转成元组。很多人背过答案但没想过不可哈希场景现场就被问住了。第45题实现LRU缓存。简单方案用OrderedDict或者直接说用functools.lru_cache。但面试官想看到手工实现思路get时把key移到末尾put时判断容量超限弹掉开头的key。能说清“为什么双向链表加哈希表是最优结构”“时间复杂度为什么是O(1)”说明你理解数据结构的工程意义。第46题反转字符串有几种方式hello[::-1]是Python风格答案。追问“不用切片怎么实现”用.join(reversed(s))或者手动双指针交换。重点不是炫技而是说清各种方式的时间和空间复杂度并解释为什么字符串不可变所以不能像列表那样原地交换。第47题大文件按行处理怎么做内存优化思路是迭代器逐行读取配合yield做流式处理避免一次性read()整块进内存。如果文件特别大还需要按块读f.read(chunk_size)加边界判断。常见场景是统计文件里每个单词出现次数、清洗CSV、导入Excel数据思路比代码重要先讲内存模型再讲实现。6.2 线上问题排查与代码走查第48~50题第48题线上程序内存不断增长怎么排查从量化定位开始先用tracemalloc对比内存快照定位增长最快的对象再看是否持有全局缓存、闭包是否意外保留了外部大对象、线程池任务是否泄漏最后用gc.get_objects()检查不可达对象。答这道题的关键不是背命令行而是给出系统化路径先量化、再定位、后修复、终验证。第49题写一段容易出bug的代码让你找问题。典型是循环中闭包引用循环变量funcs [] for i in range(3): funcs.append(lambda: i) print([f() for f in funcs])结果是[2, 2, 2]。原因闭包捕获的是变量i的引用循环结束后i已经是2。修复方式lambda ii: i用默认参数提前绑定。这道题几乎每次面试都会出现属于必须秒答的范畴。第50题如何优雅地处理嵌套数据取值避免KeyError常规做法是链式get但嵌套太深时不够优雅。Python 3.10后可以用结构模式匹配或者自己写一个递归的安全取值函数。实际工程里解析接口返回的深层JSON、配置中心数据时这个问题几乎天天碰到。面试更希望听你讲清楚“根据数据规模和容错要求选择方案”的意识而不是死记某个库。6.3 真题速查表这里整理了一份速查表把最容易出错的点汇总在一起面试前扫一眼能快速唤起记忆。题目关键词易错点正确理解isvs误以为小整数缓存适用于所有对象is比身份比值默认参数使用可变对象做默认值定义时只求值一次用None哨兵写法浅拷贝以为copy()是深拷贝copy.copy只复制第一层sort返回值以为返回排序后新列表原地修改并返回None循环闭包以为lambda捕获当前值捕获的是变量引用用默认参数绑定多线程CPU加速以为线程能利用多核GIL限制CPU并行I/O密集才合适拼接字符串循环里大量拼接性能差改用.join()读写文件忘记关闭资源用with自动管理上下文异常处理捕获后pass吞异常处理不了要显式raise键的哈希误用list做dict的keykey必须可哈希zip长度以为按最长的走按最短的可迭代对象截断这个表是我带新人时贴在团队Wiki里的东西很多坑不是一次踩完的面试前看一眼能快速唤醒记忆。再分享一个小技巧刷题过程中如果遇到“背下来了但还是理解不了”的题试着把它讲给你身边的人听或者写在便签上贴到显示器旁边过几天再回看。这类题的难点往往不是知识本身而是你还没建立从“用法”到“原理”的桥。比如不理解装饰器为什么能“偷换”函数就一直不要停在这一题先去把“函数也是对象”这一层打通回头再看装饰器就通畅了。这50道题我刷过不止一轮每次回看都能发现新的理解盲区希望你也能把它用出这种效果。