献芹奏曝-Python面试题
开篇的话:本文目的是收集和归纳常见的python面试题,希望通过以点带面、查漏补缺,发现和完善基础知识,了解常见题目套路更加从容的应对面试。希望广思集益,共同进步。
-
理论题
-
简述解释型和编译型编程语言(难度系数✯)
编译型语言: 使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。 特点: 在编译型语言写的程序执行之前,需要一个专门的编译过程,把源代码编译成机器语言的文件,如exe格式的文件,以后要再运行时,直接使用编译结果即可,如直接运行exe文件。因为只需编译一次,以后运行时不需要编译,所以编译型语言执行效率高。 总结: 1)一次性的编译成平台相关的机器语言文件,运行时脱离开发环境,运行效率高; 2)与特定平台相关,一般无法移植到其他平台; 3)现有的C、C++、Objective等都属于编译型语言。 解释型语言: 使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。 特点: 解释型语言不需要事先编译,其直接将源代码解释成机器码并立即执行,所以只要某一平台提供了相应的解释器即可运行该程序。 总结: 1)解释型语言每次运行都需要将源代码解释称机器码并执行,效率较低; 2)只要平台提供相应的解释器,就可以运行源代码,所以可以方便源程序移植; 3)Python等属于解释型语言。
属于概念型题目,多是对一些概念的考查。可参考博客C#与Python对比
-
Python解释器的种类和特点(难度系数✯)
CPython c语言开发的 使用最广的解释器 IPython 基于cpython之上的一个交互式计时器 交互方式增强 功能和cpython一样 PyPy 目标是执行效率 采用JIT技术 对python代码进行动态编译,提高执行效率 JPython 运行在Java上的解释器 直接把python代码编译成Java字节码执行 IronPython 运行在微软 .NET 平台上的解释器,把python编译成. NET 的字节码
属于理解记忆型题目
-
简述赋值深拷贝与浅拷贝(难度系数✯✯)
直接赋值:其实就是对象的引用(别名)。 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
答案简洁,如果深入了解。可参考博客浅拷贝与深拷贝
-
-
运算符
- 以点带面:相关知识可参考博客
-
求出下列逻辑语句的值(难度系数✯)
0 or 0 and 3 or 7 or 9 and 6答案主要考点:1:逻辑运算符符号之间的优先级 2:逻辑符号运算特性。
知识点/技巧:1:逻辑符号之间的优先级 (括号>not>and>or) 2:特性可简单归为:and 一假则假返前假 ,全真返后真。or 一真则真返前真。
-
求 i =10;i += i-i-1 的值(难度系数✯)
View Code主要考点:1:运算符之间的优先级 2:赋值运算符+=的特性
-
用一行代码实现 a=“hello”;b=12中a,b交换(难度系数✯)
答案主要考点:赋值运算符的灵活拓展
- 求出下列逻辑语句的值(难度系数✯✯)
weird_board = [['_'] * 3] * 3
weird_board[1][2] = 'X'
答案主要考点:1:外面的列表其实包含 3 个指向同一个列表的引用。
知识点/技巧:1:理解可变对象的引用和传递方式。
- 发送到
-
数据类型
- 以点带面:相关知识可参考博客数据类型
-
99(10进制)以八进制表示是多少(难度系数✯✯)
1:十进制 转换成 二进制 短除取余法 99/2=49...1 49/2=24...1 24/2=12...0 12/2=6....0 6/2=3....0 3/2=1....1 1/2=0.....1 结果为:余数倒排 1100011 2: 二进制 转换成 八进制 三位数一分割 1,100,011 ==》143
主要考点:1:进制转换。
知识点/技巧:1:可用 int(99,base=8)快速转换。
-
判断一个字符串是否为回文。如“雾锁山头山锁雾”(难度系数✯)
st="雾锁山头山锁雾" if (st==st[::-1]): print("回文") else: print("非回文")
主要考点:1:字符串中常用方法的灵活运用。
知识点/技巧:1:切片方法。
-
把1d2c3b4a转换成abcd?(难度系数✯)
v = '1d2c3b4a' print(v[::-2]) 字符串的切片方法。 str[起始位置索引值:终点位置的索引值:步长] 注意:1:索引值都是从0开始 2:截取的区间是[)。左闭右开 3:步长:表示每X个元素分为一组。步长为负数,表示从右向左取
主要考点:1:字符串中常用方法的灵活运用。
知识点/技巧:1:切片方法。
-
eName="aaron"; cName="逍遥小天狼";
print(eName.join(cName))的输出结果是?(难度系数✯)
逍aaron遥aaron小aaron天aaron狼
主要考点:1:字符串中常用方法的join方法。
知识点/技巧:1:x.join(y):是把前面的插入到后面。
-
dic = {"ab":"123"}
s = dic.fromkeys("bc", "456" ) 其中s的结果是?(难度系数✯✯)
{'b': '456', 'c': '456'}
主要考点:1:字典中常用方法的fromkeys方法。
知识点/技巧:1:fromkeys方法是字典的静态方法 。产生新的字典,不会对原来的字典产生影响,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值
-
a="asdfqwedd"; b="".join([x for x in a]); set(a)==set(b) 的值是多少?(难度系数✯)
True ''' [x for x in a]:列表推导式,其实 b此时的结果就是 “asdfqwedd” 通过set方法以后,去重复为{'q', 'a', 'd', 'w', 's', 'e', 'f'} '''
主要考点:1:列表表达式 2:set去重
知识点/技巧:先判断大方向其结果无非是True/False。然后在进行分析
- 统计一个字符串中,每个单词出现的频率(难度系数✯)
# 首先,引入re,进行多字符串分割 # 其次,注意大小写统一 import re message = 'To be,or not to be:that is the question' wordlist = re.split(' |:|,', message.lower()) # 方法一:创建一个计数容器,自己算 countList={} for i in wordlist: if i in countList: countList[i]+=1 else: countList[i]=1 print(countList) # 方法二:利用字典设置默认值setdefault方法 countList2={} for i in wordlist: countList2.setdefault(i,0) countList2[i]=countList2[i]+1 print(countList2) #方法三:引用第三方空间 from collections import Counter count3= Counter(wordlist) print(count3)
主要考点:1:字典属性 2:字符串的分割。3:其他相关模块的了解。
知识点/技巧:细节1:首先,引入re,进行多字符串分割。细节2:其次,注意大小写统一。
- 对[1,5,32,6,7,6,5,9,100]去重,并保持原来的顺序(难度系数✯)
l1=[1,5,32,6,7,6,5,9,100] l2=list(set(l1)) l2.sort(key=l1.index) # 注意排序没有返回值,是对其本身进行排序 print(l2) #[1, 5, 32, 6, 7, 9, 100]
主要考点:1:列表去重 2:列表排序。
知识点/技巧:细节1:首先,引入re,进行多字符串分割。细节2:其次,注意大小写统一。
- aaronList=[1,-2,-5,4,-3,5,-9,3,]; sorted(aaronList,key=lambda x:(x<0,abs(x)) ) 的结果是多少(难度系数✯✯)
aaronList=[1,-2,-5,4,-3,5,-9,3,] print(sorted(aaronList,key=lambda x:(x<0,abs(x)))) ''' [1, 3, 4, 5, -2, -3, -5, -9] 分析: 正数排前面,负数排后面。负数按照绝对值大小排列 举例:5和-5 其实是两个元组之间较量 (5<0,abs(5))==>(False,5)==>(0,5) (-5<0,abs(-5))==>(True,5)==>(1,5) 所以,5排前面,-5排后面 '''
主要考点:1:列表排序 2:元组排序。3:abs()函数。
知识点/技巧:注意执行的顺序。
-
循环
-
9*9乘法表(难度系数✯)
strResult = ""; for i in range(1,10): for j in range(1,10): if j<=i: formatResult="{0}*{1}={2}"; strResult += formatResult.format(j,i,i*j).center(10); print(strResult); strResult="";
主要考点:1:循环
-
冒泡排序(难度系数✯)
intList = [12, 3, 54, 46, 75, 86, 89, 97, 6, 2] for i in range(0, len(intList)-1): # 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 [1] # 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 [2] # 针对所有的元素重复以上的步骤,除了最后一个。 [3] # 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 [4] minInt = intList[i] #定义一个最小的数, for j in range(i, len(intList)): if intList[i] > intList[j]: # 交换位置 minInt=intList[j] intList[j] = intList[i] intList[i] = minInt print(intList) '''输出结果 [2, 12, 54, 46, 75, 86, 89, 97, 6, 3] [2, 3, 54, 46, 75, 86, 89, 97, 12, 6] [2, 3, 6, 54, 75, 86, 89, 97, 46, 12] [2, 3, 6, 12, 75, 86, 89, 97, 54, 46] [2, 3, 6, 12, 46, 86, 89, 97, 75, 54] [2, 3, 6, 12, 46, 54, 89, 97, 86, 75] [2, 3, 6, 12, 46, 54, 75, 97, 89, 86] [2, 3, 6, 12, 46, 54, 75, 86, 97, 89] [2, 3, 6, 12, 46, 54, 75, 86, 89, 97] '''
主要考点:1:循环 2:冒泡排序。
知识点/技巧:1:假设第一个元素是最小值,2:如果发现后面的元素是更小的与之交换位置。3:假设第二个元素是最小值,循环往复
-
-
函数
- 相关知识可参考博客变量与函数
-
一行代码计算出1+2+3......+100的和(难度系数✯)
# 方法一 from functools import reduce print(reduce(lambda x, y: x + y, range(101))) # 方法二 print(sum(x for x in range(101)))# 方法三 print(sum(range(101)))
主要考点:1:列表推到式 2:内置函数。
知识点/技巧:1:先生成可迭代对象。2:再对可迭代对象进行求和迭代
-
请写出此代码输出结果(难度系数✯)
1 a = 1 2 def fun_1(): 3 a = 2 4 def fun_2(): 5 nonlocal a 6 a = 3 7 def fun_3(): 8 a = 4 9 print(a) 10 print(a) 11 fun_3() 12 print(a) 13 print(a) 14 fun_2() 15 print(a) 16 print(a) 17 fun_1() 18 print(a)
1 a = 1 # 执行顺序 1 2 def fun_1(): # 执行顺序 2 3 a = 2 # 执行顺序 5 4 def fun_2(): # 执行顺序 6 5 nonlocal a # 执行顺序 9 注意:nonlocal用法是获取最近区域的a值。也就是第三行a等于2 6 a = 3 # 执行顺序 10 注意:此时修改同时修改第三行a。等于3 7 def fun_3(): # 执行顺序 11 8 a = 4 # 执行顺序 14 9 print(a) # 执行顺序 15 输出:4 10 print(a) # 执行顺序 12 输出:3 11 fun_3() # 执行顺序 13 12 print(a) # 执行顺序 16 输出:3 13 print(a) # 执行顺序 7 输出:2 14 fun_2() # 执行顺序 8 15 print(a) # 执行顺序 17 输出:3 16 print(a) # 执行顺序 3 输出:1 17 fun_1() # 执行顺序 4 18 print(a) # 执行顺序 18 输出:1 19 20 ''' 21 1 22 2 23 3 24 4 25 3 26 3 27 1 28 '''
主要考点:1:变量作用域 2:nonlocal 用法。
知识点/技巧:1:变量使用遵守就近原则。2:nonlocal 表⽰在局部作⽤域中, 调⽤⽗级命名空间中的变量
-
请写出此代码输出结果(难度系数✯✯)
def addList(par,list=[]): list.append(par) return list l1=addList(11) l2=addList(22,[]) l3=addList("33") print(l1) print(l2) print(l3)
[11, '33'] [22] [11, '33']
主要考点:1:函数默认参数。
知识点/技巧:1: 默认值只会执行一次。
-
利用多种方法实现二分查找66(难度系数✯✯)
lis=[0, 2, 11, 42, 44, 66, 67,68,69, 77, 88, 100,123, 153, 456] #方式一:直接代码编写 # 定义检索的边界 left = 0 right=len(lis)-1 n=66 findCount=0 while left<right: findCount +=1 mid=(left+right)//2 if lis[mid]==n: print("查找到了:%s次。%s在列表的 %s位置" % (findCount,n,mid)) break elif lis[mid]>n: right=mid else: left=mid else: print("不存在!") #方式二:递归 def midfind(lis,n): if not lis or len(lis)==1: print("您要查找的元素不存在!") return mid=len(lis)//2 #查看中间元素 if lis[mid]==n: print("查找到了") return elif lis[mid]<n: midfind(lis[mid:],n) elif lis[mid]>n: midfind(lis[:mid],n) midfind(lis,68)
主要考点:1:编程的思维
知识点/技巧:1: 可使用递归。
- 请写出此代码输出结果(难度系数✯✯)
def change(val): val.append(100) val = ['T', 'Z', 'Y'] nums = [0, 1] change(nums) print(nums)
[0, 1, 100]
主要考点:1:Python函数参数的传递方式
知识点/技巧:1:Python中函数参数的传递是传递的变量的值,即就是变量所指向的对象的地址
- 待续
-
闭包、迭代器、生成器、装饰器
- 相关知识可参考博客闭包、迭代器、生成器、装饰器
-
请写出此代码输出结果(难度系数✯✯✯)
def my_func(*args): fs = [] for i in range(3): def func(): return i * i fs.append(func) return fs fs1, fs2, fs3 = my_func() print(fs1()) print(fs2()) print(fs3())
4 4 4
主要考点:1:函数。2:闭包。
知识点/技巧:1:理解python解释器运行原理,通过画图分析。鉴于篇幅问题,详细说明请参考
-
请写出此代码输出结果(难度系数✯✯✯)
def add(a, b): return a + b def test(): for r_i in range(1,4): yield r_i g=test() for n in [2,10]: g=(add(n,i) for i in g) print(list(g))
#[21, 22, 23] #根据生成器惰性机制,只有在list(g)的时候才开始调用,此时g的内容是什么? # 当n=2 时 g=(add(n,i) for i in g) 但是此时仍然没有执行 # 当n=10 时 g=add(n,i) for i in (add(n,i) for i in g) ),开始执行。 # 内存计算后得:g=add(n,i) for i in [11,12,13] # 再次计算可得:[21,22,23]
主要考点:1:生成器的惰性机制。
知识点/技巧: for n in [2,10]: 其中该列表的个数相当与循环的次数,列表中最后一个元素代表累加的步长。
for r_i in range(1,4) 的范围表示最终list中有多少个元素,其中起始值代表基数。
举例:如果 for n in [2,10] 变成 [100,200,300,5] 表示循环以5为基数,循环4次
for r_i in range(1,4) 变成 (10,20) 表示最终结果为9个元素,
一句话:[10,11....19],以5为步长,连加4次.结果为:[30,31....39]
-
请实现一个装饰器, 计算函数的运行时间(难度系数✯✯)
import time def timmer(func): def wrapper(*args,**kwargs): startTime= time.time() res=func(*args,**kwargs); endTime= time.time() print("被测试函数一共运行:"+str(endTime-startTime)+"秒") return res return wrapper def test(a,b): return a+b print(test(100,200))
主要考点:1:装饰器结构。
知识点/技巧:装饰器=高阶函数(传入参数或输出结果是一个函数)+函数嵌套(函数中定义函数)+闭包
-
请实现一个装饰器,完成以下功能(难度系数✯✯✯)
Write a decorator which: Prints the execution time of a function Accepts a parameter max_time: int ; when the function execution time exceeds max_time, prints an extra warning @timeit(max_time=1) def func(*args, **kw): time.sleep(2) # Output in stdout: # "func" function ran for 2s # "func" function exceeds max_time
1 import time 2 import functools 3 4 5 def timeit(max_time=1): 6 ''' 7 Prints the execution time of a function 8 :param max_time: when the function execution time exceeds max_time , prints an extra warning 9 :return: function 10 ''' 11 12 def cell_fun(func): 13 @functools.wraps(func) 14 def clocked(*arg, **kwargs): 15 t0 = time.perf_counter() 16 _result = func(*arg, **kwargs) 17 elapsed = time.perf_counter() - t0 18 print('"%s" function ran for %ds' % (func.__name__, elapsed)) 19 if elapsed > max_time: 20 print('"%s" function exceeds max_time' % (func.__name__)) 21 return _result 22 23 return clocked 24 25 return cell_fun
主要考点:1:装饰器结构。2:装饰器传参
知识点/技巧: 装饰器相关内容,可参考 https://www.bilibili.com/video/BV18i4y1u7Cr?p=23
-
生成一个[[0, 0, 0], [0, 1, 2], [0, 2, 4], [0, 3, 6]]的列表(难度系数✯)答案
主要考点:1:列表推导式。
知识点/技巧:列表推到式相当于for循环,前面相当于循环结果,可以嵌套
- 装饰器嵌套与传参
def add(value): def decorator(func): def wrapper(x): result = func(x) return result + value return wrapper return decorator def sub(value): def decorator(func): def wrapper(x): result = func(x) return result - value return wrapper return decorator def mul(value): def decorator(func): def wrapper(x): result = func(x) return result * value return wrapper return decorator @mul(4) @add(6) @sub(3) def yango(number): return number # 示例用法 result = yango(5) print(result) # 输出 (((5-3)+6)*4)= 32
主要考点:1:装饰器嵌套。2:装饰器传参
-
-
面向对象
- 相关知识可参考博客面向对象
-
请写出此代码输出结果(难度系数✯✯)
class A: def func(self): print("A") class B(A): def func(self): super().func() print("B") class C(A): def func(self): super().func() print("C") class D(B, C): def func(self): super().func() print("D") d = D() d.func()
A C B D A B C D 继承顺序:新式类,广度优先 D==》B ==》C===》A
主要考点:1:面向对象。2:继承顺序。
知识点/技巧:1:多继承中新式类的继承顺序是广度优先
-
请写出一个单例模式(难度系数✯✯)
class Single: ISINCTANCE=None def __new__(cls, *args, **kwargs): if not cls.ISINCTANCE: cls.ISINCTANCE = object.__new__(cls) return cls.ISINCTANCE def __init__(self, *args, **kwargs): pass a = Single() print(a) b = Single() print(b) ''' 输出结果 <__main__.Single object at 0x00F5FE70> <__main__.Single object at 0x00F5FE70> '''
主要考点:1:单例模式。2:内置方法__new__。3:类初始化的原理
知识点/技巧:1:__new__ 方法。2:先定义属性,new的时候判断该属性是否有值,如果有值返回。没有值再new值单例
-
员工去重
# 公司内存在1000个员工,但由于调岗等原因导致员工数据重复 # 如果几个员工对象的姓名和性别相同,这是一个人 # 请对这1000个员工做去重
class Employee: def __init__(self, name, sex, age, dept): self.name = name self.sex = sex self.age = age self.dept = dept def __hash__(self): return hash('%s%s' % (self.name, self.sex)) def __eq__(self, other): if self.name == other.name and self.sex == other.sex: return True employ_lst = [] for i in range(1000): if i % 3 == 0: employ_lst.append(Employee('张三', 'male', i, 'python')) elif i % 3 == 1: employ_lst.append(Employee('李四', 'male', i, 'python')) else: employ_lst.append(Employee('王五', 'male', i, 'python')) employ_newList = set(employ_lst) for i in employ_newList: print(i.name, i.sex, i.age) ''' 1:定义一个员工类(Employee) 2:对象的属性 : 姓名 性别 年龄 部门 3:重写__hash__方法 4:重写__eq__方法 5:初始化数据 6:set去重 ''' '''输出结果 李四 male 1 张三 male 0 王五 male 2 '''
主要考点:1:set去重原理。2:内置方法__hash__、__eq__
知识点/技巧:去重原理:set() 函数中会先调用对象的 __hash__() 方法,获取 hash 结果;如果 hash 结果相同,进一步调用函数 __eq__()判断二者的值是否相等;
- 相关知识可参考博客面向对象
-
编程
- 手写一个类似字典行为的类(难度系数✯✯✯)
Complete the following class. Emulate the key access & assignment behaviour of a dict without using the Python built-in dict class CustomDict: pass # Expected results: cdict = CustomDict() cdict["a"] = 1 cdict["b"] = 2 print(cdict["a"]) # 1 print(cdict["c"]) # KeyError
import collections class CustomDict: def __init__(self): self.lst = [] self.CDict = collections.namedtuple('CDict', ['key', 'value']) def __getitem__(self, key): for item in self.lst: if item.key == key: return item.value else: return "KeyError" def __setitem__(self, key, value): new_dict = self.CDict(key,value) for item in self.lst: if item.key == key: self.lst.remove(item) break else: self.lst.append(new_dict) cdict = CustomDict() cdict["a"] = 1 cdict["b"] = 2 print(cdict["a"]) # 1 print(cdict["c"]) # KeyError
主要考点:1:思维逻辑。2:内置方法__getitem__、__setitem__
知识点/技巧: 答案不唯一,这里采用的是具名元组手写一个类似字典行为的类
- 类似数独的填字游戏(难度系数✯✯✯✯)
Implement a function def init_board() -> List[List[int]]: which returns a 6 by 6 matrix that fulfills the following requirements: The 36 numbers on the board must be 9 ones, 9 twos, 9 threes and 9 fours Any row or column must not contain 3 or more direct neighbours that are the same number The function return value must not be a constant Obviously it’s not allowed to use pre-calculated answers Correct example answers: [[3, 2, 4, 1, 3, 2], [2, 2, 1, 1, 4, 4], [4, 4, 1, 3, 3, 2], [4, 1, 3, 2, 2, 4], [3, 1, 2, 4, 3, 1], [3, 3, 1, 1, 2, 4]] [[3, 3, 1, 2, 2, 4], [1, 1, 3, 3, 2, 4], [4, 4, 2, 1, 1, 3], [2, 2, 3, 4, 4, 1], [4, 4, 1, 1, 2, 2], [3, 1, 2, 3, 3, 4]]
import random import numpy from typing import List def get_board_by_size(size: int): board = numpy.array([0 for i in range(size * size)]) board.shape = size, size return board def get_exist_num(row, col, board): # exist_row = board[row] # exist_col = board[:, col] return list(board[row]) + list(board[:, col]) def get_valid_num(exist_list): choice_list = [1, 2, 3, 4] * 2 # 可选项 [choice_list.remove(e) for e in exist_list if e in choice_list] cur_value = 0 if choice_list: if len(choice_list) == 1: cur_value = choice_list[0] else: cur_value = random.choice(choice_list) return cur_value def set_point_num(row, col, board): ''' 给每一个棋盘中的值设置点 :param row: 行 :param col: 列 :param board: 棋盘 :return: None ''' exist_list = get_exist_num(row, col, board) valid_num = get_valid_num(exist_list) board[row, col] = valid_num def set_board_value(board, rule=None): cols, rows = board.shape # type:numpy [set_point_num(row, col, board) for row in range(rows) for col in range(cols)] def init_board() -> List[List[int]]: board = get_board_by_size(6) # 初始化一个棋盘 set_board_value(board) # 设置棋盘值 global cur_try # 当前尝试次数,ps:可以做参数传递 while numpy.any(board == 0) and cur_try < MAX_TRY: # 如果给出的结构中有 0,表示答案有误,重新初始化棋盘 board = init_board() if not numpy.any(board == 0): return board cur_try += 1 return board MAX_TRY = 100 # 可以从公共常亮中获取 cur_try = 0 board = init_board() print(board)
主要考点:1:思维逻辑。2:numpy 库
知识点/技巧: 答案不唯一,这里采用的是通过numpy来实现
- 手写一个类似字典行为的类(难度系数✯✯✯)
-
算法
- 未完