ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python面试题55道解析:从背答案到看懂考察机制

Python面试题55道解析:从背答案到看懂考察机制

简介:面向准备Python技术面试的初中级开发者与求职者,整理55道常见面试题及参考答案,涵盖语法基础、数据结构、函数作用域、循环嵌套、装饰器、异常处理、字典与文件操作、正则匹配、logging日志、JSON序列化等高频考点。内容以题目、代码片段、输出结果和解析相结合的方式呈现,便于读者理解执行顺序、易错细节与标准答题思路,也可作为日常查漏补缺的复习清单。资源包共1个docx文档,约80KB,轻量便于下载后直接阅读和检索;已有1108人学习下载。对需要快速熟悉Python面试题型、梳理知识框架或临考复盘的人来说,可从中获得较完整的题目索引、答案参照和知识点串联价值。

1. 面试题不是背答案:Python 面试题到底在考什么

很多人第一次刷 Python 面试题,心态跟背单词差不多:把答案记住,面试时对上了就行。可真到面试现场,面试官把代码一改,问一句“那这里为什么会报错”,脑子就空了。这跟背菜谱却不会开火是一个道理——你记住的是“这道菜长什么样”,没搞懂“火候为什么这么走”。

Python 面试题及答案这类资料,本质不是题库,而是一张知识地图:它把语法、数据结构、作用域、装饰器、异常处理、面向对象、文件操作这些散点,用一道道小题串成线。题目本身会过时,但题背后的机制不会。搞懂一道题,等于点亮一个概念;搞懂一类题,等于打通一条链路。这篇就借一份 55 道规模的 Python 面试题资料,把“面试题究竟在考什么”讲透,让你从“背答案”切换到“看懂出题人的意图”。

2. 为什么面试官爱出这些题:从一段代码看考察逻辑

2.1 一道“找不重复元素的和”暴露的两种思维

资料里第一题就很典型:给一个数组,求其中没有重复出现的那些值的和。原题给了两版写法,第一版用两个列表分别记录“已收集”和“已作废”的元素,逐个遍历、判断、移除;第二版一行搞定:

def func2(lis): # set 去重,count 判断该元素在原列表中是否只出现一次 return sum([i for i in set(lis) if lis.count(i) == 1]) print(func2([3, 4, 1, 2, 5, 6, 6, 5, 4, 3, 3])) # 输出 1 + 2 = 3

这段代码短,但信息量不小。set(lis)先把重复元素压掉,count(i) == 1再筛出“原本只出现一次”的那些,最后sum求和。参数上没什么可调的,但面试官真正想看的是:你会不会用集合这个数据结构,你知不知道count的复杂度是线性的、放在列表推导里其实是 O(n²),以及你能不能说出“短不等于快”。第一种写法虽然啰嗦,但只遍历一遍;第二种写法漂亮,元素一多就翻车。这就是面试题的第一层考察:不是问你“能不能写出来”,而是问你“知不知道自己写了什么代价”。

2.2 作用域那道题:为什么a += 3会直接报错

资料里有一道看着莫名其妙的题:

a = 1 def bar(): a += 3 bar() print(a)

答案是运行出错。很多人第一反应是“缩进错了吧”,其实问题在作用域规则上。函数体里一旦出现对a的赋值语句,Python 在编译阶段就把a判定为这个函数的局部变量;可a += 3又需要先读a的旧值,局部变量还没赋值就要读,于是抛出UnboundLocalError。想改全局的a,得在函数里先声明global a。

这道题的价值不在答案,而在它逼你区分两个概念:名字的绑定发生在编译期,值的读写发生在运行期。搞清这一点,后面遇到闭包、装饰器里“为什么外层变量能记住”时就不会懵。面试官出这类题,是在筛“有没有真正理解 Python 的对象模型”,而不是筛“记不记得 global 这个关键字”。

2.3 装饰器与异常:执行顺序为什么总记反

资料里那段双层装饰器的代码,输出顺序常常让人栽跟头:

def decorator_a(func): print('Get in decorator_a') def inner_a(*args, **kwargs): print('Get in inner_a') return func(*args, **kwargs) return inner_a def decorator_b(func): print('Get in decorator_b') def inner_b(*args, **kwargs): print('Get in inner_b') return func(*args, **kwargs) return inner_b @decorator_b # 等价于 f = decorator_b(decorator_a(f)) @decorator_a def f(x): print('Get in f') return x * 2 f(1)

记住两句话就够了:装饰发生在函数定义完成的那一刻,从下往上套;调用发生在你写f(1)的时候,从上往下剥。所以先打印decorator_a、再打印decorator_b(套的顺序),调用时先进inner_b、再进inner_a、最后才是f本体。这个“定义时自下而上、调用时自上而下”的规律,是装饰器类面试题的统一钥匙,比死记某一段输出靠谱得多。

异常处理那道题同理:try里抛异常、except捕获并return,finally照样打印ok。finally的语义是“无论如何都要走一遍”,它是清理资源的兜底通道,不是异常分支的一部分。理解这一点,你才知道为什么文件操作、锁、数据库连接都爱把它(或with)挂在后面。

2.4 从语法题到工程题:文件遍历、正则与日志的分量

资料后半段开始从“语法判断”转向“工程能力”。比如遍历文件夹下所有文件路径,给了两种写法:一种用os.walk逐层产出(root, dirs, files)三元组再拼接路径,一种用os.listdir配合递归。前者是迭代器思路,一次只吐一层,内存友好;后者靠函数自己调自己,代码直观但层级深了容易踩递归上限。

再比如正则:match只从字符串开头匹配,search会扫描整串;默认量词是贪婪的,尽量多吞字符,量词后加个?才变成非贪婪。还有日志模块的意义、json序列化时datetime要自定义编码器、中文要加ensure_ascii=False才不被转成转义序列。这些点单拎出来都不难,但它们共同指向一件事:面试官在确认你有没有写过真正会跑很久、会出错的代码。语法题考记忆,工程题考经验。

3. 题目背后的知识骨架:几张必须拎清的概念对照

3.1 数据结构与容器:列表、集合、字典各管一摊

资料里反复出现的几个容器,其实分工明确。列表管顺序、允许重复,按下标取值快,按值查找慢;集合管去重和成员判断,in操作接近常数时间,但无序;字典管映射,键唯一,查找同样高效。第一题用set去重、用count计数,就是把“去重”和“计数”两件事分别交给合适的工具——如果元素量级大,更稳的写法是先用字典统计频次,再挑出次数为 1 的键求和,整体只扫一遍。这类“换个容器复杂度就掉一个量级”的判断,是面试题里最高频的隐性考点。

3.2 面向对象那一串:继承、封装、多态与鸭子类型

面向对象部分题量最大,也最容易背串。继承是子类直接获得父类的属性与方法;封装是把属性方法收进类里、用双下划线做名字改写(并不是真正“藏起来”,而是变成_类名__属性的形式);多态是不同对象调同一个方法得到不同结果,Python 里连+对列表、字符串、数字行为不同都算多态的体现。

鸭子类型则是动态语言特有的判断方式:一个对象合不合格,不看它继承自谁,而看它有没有需要的方法和属性——“走起来像鸭子、叫起来像鸭子,那就是鸭子”。与之配套的还有super()(子类想同时调用父类同名方法时用)、MRO 与 C3 算法(多继承时方法查找的顺序规则)、单下划线与双下划线的区别(前者是约定俗成的“内部使用”,后者触发名字改写)。这一串概念像一条链,缺一环后面就接不上。

3.3 特殊方法与魔术方法:双下划线不是玄学

资料列了一串双下划线方法:__new__负责造对象、__init__负责初始化、__call__让实例能像函数一样加括号调用、__str__决定print一个对象时显示什么、__getattr__在属性找不到时兜底、__delattr__在删属性时触发。它们不是语法糖摆设,而是 Python 把“对象在各种场合该有什么行为”开放给开发者定制的接口。

一个常见的困惑是__new__和__init__谁先谁后:先__new__造出实例,再__init__往实例上挂数据。单例模式的两种写法正好用得上这组知识——一种重写__new__加锁保证只造一次,一种用元类在__call__里拦住创建过程。资料把两种都给了,本质是在说:同一个需求,可以从“造对象”这一层拦,也可以从“类怎么被调用”这一层拦。

3.4 代码执行顺序与作用域链:从编译期到运行期

把前面几题串起来看,会发现一个隐藏主线:Python 的执行不是从上到下一条直线。函数定义时,装饰器立即执行、闭包捕获外层名字;函数调用时,局部作用域、闭包作用域、全局作用域、内置作用域按 LEGB 顺序查找;异常抛出时,try、except、finally各按自己的语义走一遍,finally永远收尾。把这些顺序理清,那些“输出结果是什么”的题就不再靠运气。

4. 容易搞混的几组概念:初学者最常翻车的地方

4.1 易混点一:is和==不是一回事

很多人混,是因为小整数和短字符串的缓存让人觉得“两个看起来一样的东西就是同一个”。区分方法很直接:==比的是值,is比的是内存地址是不是同一个对象。判断类型是否相同要用type(a) == A(或更推荐的isinstance),判断两个变量是否指向同一个对象才用is。资料里isinstance与type那道题就在提醒:子类实例用type去比父类会得到False,而isinstance会返回True,因为后者考虑继承关系。

4.2 易混点二:实例方法、类方法、静态方法

三者都是定义在类里的函数,差别在第一个参数和装饰器。实例方法第一个参数是self,只能由实例调用;类方法用@classmethod,第一个参数是cls,实例和类都能调,常用于写备用构造器;静态方法用@staticmethod,既没有self也没有cls,本质是挂在类命名空间下的普通函数。混的原因多半是“都能通过类名调用”,但能不能碰实例数据、能不能碰类数据,是三条完全不同的路。

4.3 易混点三:实例变量与类变量

实例变量是每个对象自己的一份,类变量是所有实例共享的一份。容易出错的地方在于:通过实例去改类变量,实际上会在实例上新建一个同名属性,类变量本身岿然不动;只有通过类名去改,才是真的改共享值。这个差别在计数器、配置默认值这类场景里会直接导致结果对不上。

4.4 易混点四:浅拷贝与深拷贝、可变与不可变

字典那道题func(mydict)改完mydict后外面看到的是{'a': 0, 'b': 2, 'c': 2},原因是字典是可变对象,函数里拿到的是同一个对象的引用,改内容就是改本体。换成一个整数传进去,函数里怎么改外面都不变。混的根源是没建立“变量是标签、对象才是实体”的直觉。一旦接受这个模型,拷贝、传参、默认参数写成可变对象这类坑就都能提前躲开。

4.5 易混点五:match与search、贪婪与非贪婪

正则里match只在字符串开头尝试,开头不匹配直接None;search会扫全串找第一个命中。贪婪匹配是“能多拿就多拿”,量词后加?变成非贪婪,“够用就停”。混的原因是两者都“返回匹配结果”,看起来只差个函数名,实际匹配范围和结果长度完全不同。写邮箱、提取标签内容这类题,选错一个就是空结果或者吞掉一大段。

5. 什么时候该用这份资料:一个具体场景和几条边界

假设你正在准备一次 Python 岗的面试,简历上写着“熟悉 Python”,但心里清楚自己只是“用过”。这时候最有效的路径不是从语法书第一页翻起,而是拿一份成体系的题单做诊断:先扫一遍题目,凡是看到就心里发虚的,标记出来;再按标记去补概念,每补完一块回来重做那道题,看能不能讲出“为什么这样写、换个条件会怎样”。

这份 55 道规模的资料适合的场景很明确:语法自检、概念补漏、面试前的口头表达训练。它的题目覆盖了数组去重求和、变量作用域、多层循环枚举、装饰器执行顺序、异常与finally、字典引用传递、目录遍历、正则匹配、日志、json序列化、面向对象三大特性、鸭子类型、super与 MRO、魔术方法、单双下划线、三种方法类型、isinstance与type、with上下文管理、单例模式、可调用对象、栈与队列的手写实现、单链表逆置、类的加载过程、OSI 七层模型等一串点,正好是一张“初级到中级”的知识网。

它不适合的场景也得说清:如果你要的是某个框架的实战、性能调优的深度方案,或者想靠背题混过一轮,这份资料帮不上。题目里的代码都是独立小片段,没有工程上下文;答案给的是结论,讲不透的地方需要你自己回去翻文档、动手跑一遍。真正把它用出价值的姿势是——每道题都亲手敲一遍,改几个参数看输出怎么变,尤其是装饰器、作用域、可变对象这几类,光看必翻车。

我自己的习惯是:做题时先不看答案,把预测的输出写在纸上,跑完对一遍,错的题在旁边写一句“我原来以为……其实……”。这一句就是你的认知缺口。那份资料里“找不重复元素的和”我第一遍就写了个双重循环,觉得挺对,直到看到集合那版才意识到自己压根没想过复杂度。从那以后我每次看这类题,都强制先问一句“换个数据量还成立吗”,再动手写。希望这份思路帮到你,资料在,题在,剩下的是你亲手跑一遍。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进