笔者担任 Python 面试官多年,积累了很多面试题,特整理起来,希望对求职者有所帮助。此外,我们从网上摘录了很多经典面试题,配以详尽的讲解,举一反三。
我们将不定期更新,订阅可以关注我们的公众号: 小菜学编程 。
面试题
- 用一行代码实现整数 1 至 100 之和
网上的答案是通过 range 生成 1 至 100 的整数,然后用 sum 求和:
|
|
这行代码确实很有美感,但你想过没有:如果是求 1 至 10000000000 之和呢?候选人必须认识到这是一个 O(N) 算法,真的适合所有场景吗?为什么不用等差数列前 N 项和公式进行计算呢?
|
|
采用前 N 项和公式,求和时间复杂度是 O(1) ,孰优孰劣应该很明显了吧。大家可以对比下当 N 很大时,这两种计算方式的表现:
|
|
面试官喜欢引申,候选人如果只是刷题记答案而不会分析,肯定是过不了关的。
- 如何在一个函数内部修改全局变量
在函数内部用 global 关键字将变量申明为全局,然后再进行修改:
|
|
面试官还可能引申到以下概念讨论,必须滚瓜烂熟:
- 变量作用域 ( scope )
- 局部名字空间 ( locals )
- 闭包名字空间 ( globals )
- 全局名字空间 ( enclosing )
- 内建名字空间 ( builtin )
- 请描述执行以下程序将输出什么内容?并试着解释其中的原因。
|
|
|
|
这有点令人丈二和尚摸不着头脑,明明默认参数是一个空列表,为什么第 2 、 3 次调用后,列表都比预期中多一些数值呢?这一切得从 Python 函数的运行机制说起—— Python 函数默认参数是如何实现的?
Python 函数在创建时便完成了默认参数的初始化,并将默认参数保存在函数对象的 __defaults__ 字段中:
当我们调用 add(1) 时,Python 虚拟机创建一个 栈帧 对象 PyFrameObject ,用于保存函数执行过程中的上下文信息。栈顶对象保存函数局部变量以及一个运行栈,Python 虚拟机负责从函数对象中取出默认参数并设置相关局部变量:
add(1) 执行完毕后,作为函数默认参数的那个 list 对象,就包含了一个元素 1 :
当我们再次调用 add(2) 时,Python 虚拟机还是从函数对象中取出这个 list 对象作为 l 的默认参数。因此,第二个 print 语句输出 [1, 2] 也就不奇怪了。
总结起来,默认参数在函数对象创建时便完成了初始化,并保存在函数对象中。当函数被调用时,Python 从函数对象中取出默认参数,而不是重新初始化。因此,无论 add 函数被调用多少遍,默认参数总是同一个 list 对象。
这与我的直观感觉相悖,因此尽量不要用可变对象作为默认参数,以避免一些潜在的 BUG 。如果实在无法避免,则可以换一种更的严谨写法:
|
|
当 add 函数被调用时,如果参数 l 未指定,Python 自动使用默认值 None 。函数内部对参数 l 进行判断,如果它的值是 None ,便将其设为一个新的空列表。这样,add 函数的行为就更符合我们的预期了。
- 列出 5 个 Python 标准库
这是一个开发性题目,面试官以考察候选人知识面以及学习深度为目的。必须结合自身情况,选择一些自己比较熟悉的标准库作答,面试官随时可能深入讨论。
保险一点,可以回答一些常用但很浅显的,例如:
想要获得加分,也可以回答一些高级的,例如:
- os ,系统调用
- socket ,套接字编程与网络通讯
- threading ,多线程处理
- multiprocessing ,多进程处理
- queue ,同步任务队列
面试官很有很能深入提问,切记:如果自己不是很熟悉,就不要班门弄斧了。
- 字典如何删除键
方法一 ,使用 del 语句进行删除, del 关键字还可用于删除 变量 、 属性 :
|
|
方法二 ,调用 pop 方法进行删除,这样可以拿到被删除键对应的值:
|
|
- 如何合并两个字典
|
|
方法一 ,调用 dict 对象 update 方法:
|
|
方法二:
|
|
- Python 2 和 Python 3 中的 range(100) 的区别
Python 2 中的 range 函数返回一个列表,长度越大消耗内存越多:
|
|
Python 2 中的 xrange 函数与 range 类似,但返回 生成器 :
|
|
生成器内存消耗固定,与长度无关。因此,循环一般使用 xrange :
|
|
由于生成器比较高效, Python 3 的 range 函数也选择返回生成器,可以认为与 Python 2 中的 xrange 等价:
|
|
当然了,Python 3 中也可以实现与 Python 2 中的 range 函数一样的效果:
|
|
- Python 列表如何去重
|
|
先将列表转换成 集合 ( set ),由于集合元素不重复,便实现去重:
|
|
最后再将集合转化成列表即可:
|
|
- 一句话解释什么样的语言能够用装饰器
函数可以 作为参数传递 、 可以作为返回值返回 的语言,都可以实现装饰器。
- Python 内建数据类型有哪些
- 布尔 , bool
- 整数 , int
- 浮点 , float
- 字符串 , str
- 字节序列 , bytes
- 元组 , tuple
- 列表 , list
- 字典 , dict
面试官可进一步延伸到对象 内部结构 ,相关操作 时间复杂度 等高级知识点。
- 请设计正则表达式,提取标签里的内容(中国),注意 class 名是不确定的:
|
|
这个题目考察根据 html 标签结构,编写正则表达式。参考答案如下:
|
|
需要特别注意,类名中不能包含双引号,而标签中的文本不能包含小于号。
- 请编写正则表达式,提取以下网页中所有 a 标签的 URL
|
|
这题目考察标准库 re 模块的基本用法,难度不高,根据文本特征写正则即可:
|
|
注意到,参考答案中的正则表达式匹配 a 开标签,括号表示 内容提取 。 正则表达式在日常开发中应用场景很多,必须完全掌握。
- Python 中有几个名字空间,分别是什么
Python 总共有 4 个名字空间:
- 局部名字空间 ( locals )
- 闭包名字空间 ( closures )
- 全局名字空间 ( globals )
- 内建名字空间 ( builtin )
- 以 with 关键字打开并处理文件有什么好处
调用 open 函数打开文件,得到一个文件对象,里面包含打开的文件描述符或文件句柄。我们对文件对象进行读写操作后,必须关闭文件对象,不然就会造成进程句柄泄露。
|
|
由于读写、处理数据时可能出错,一旦程序抛异常,关闭文件那行代码便没机会执行。因此,需要将可能抛异常的代码写成 try 结构,在 finally 中关闭文件:
|
|
这样一来,不管 try 里面那两行代码会否抛异常,程序最终总会执行 f.close() ,杜绝了文件泄露的可能。 try 结构不够简洁,我们还可以通过 with 关键字实现:
|
|
Python 进入 with 代码块前,自动调用 f.enter ;离开 with 代码块后,自动调用 f.exit ;而 f.exit 则负责关闭自己。因此, with 同样可以保证打开的文件对象最终被关闭,但却比 try 结构简洁很多。
- 举例说明 Python 中断言( assert )的用法
|
|
- 请处理以下字符串,先将字符去重,再按 ASCII 排序,最后输出结果
|
|
首先,借助集合 set 完成字符去重:
|
|
然后,调用 sorted 函数对去重结果进行排序:
|
|
最后,调用字符串对象 join 方法,将多个字符拼接成完整的字符串并输出:
|
|
- 请统计以下字符串中每个字符出现的次数
|
|
借助 collections 模块中的 Counter ,只须一行代码即可完成统计:
|
|
此外,使用 collections 模块中的 defaultdict 也可以实现:
|
|
defaultdict 需要一个可调用对象作为参数,当你访问一个不存在的 key 时, defaultdict 自动调用该对象生成默认值并进行插入:
|
|
当然了, 你用一个原始的 dict 字典,同样可以实现:
|
|
像这样没有标准答案的题目,可以考察候选人的编程水平。这 3 种不同方法,虽然都能解决问题,水平却可分高下。如果你把代码写得如此憋足,就算完全正确,面试官内心肯定还是嫌弃的:
|
|
将代码写得更 Pythonic 些,也是一门学问,很重要的!
- 用 lambda 函数实现一个乘法操作函数,计算两个参数的乘积
|
|
- 现有字符串 s ,每个单词中间是空格,请过滤英文和数字,最终输出 “张三 深圳”
|
|
首先,我们以空格为分隔符,将字符串分割成若干单词:
|
|
然后,我们用正则把只包含英文和数字的单词提取出来,并保存到集合 words :
|
|
接着,我们将只包含单词和字母的单词过滤掉:
|
|
最后,将剩下单词拼接起来:
|
|
- 用 filter 函数求出以下数列中所有奇数并保存到新列表
|
|
首先,我们定义一个函数 odd ,判定给定数值 n 是否为奇数:
|
|
然后,我们以 odd 为判定函数过滤数列 numbers ,并将结果保存到新列表:
|
|
- 用列表推导的方法求出以下数列中所有奇数并保存到新列表
|
|
|
|
- re 模块中的 compile 函数有什么作用?
re.compile 函数将正则表达式编译成一个对象,以避免重复编译,提高执行效率。
- 下列 3 个变量分别是什么数据类型?
|
|
- a 是一个 元组 tuple ,元组里包含一个元素,即整数 1 ;
- b 是一个 整数 int ,
(1)
等价于1
; - c 是一个 字符串 str ,
("1")
等价于"1"
;
|
|
-
试谈一下 Python 解释器中的全局锁 GIL
-
**如何理解 func(*args, *kwargs) 中的 *args 和 *kwargs
【小菜学Python】系列文章首发于公众号【小菜学编程】,敬请关注: