(1)Python常见面试题1
1.Python是如何进行内存管理的?
三个方面来回答这个问题;
①对象的引用计数机制
②垃圾回收机制
③内存池机制
(1)对象的引用计数机制
Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。
引用计数增加的情况:
1.一个对象分配一个新名称
2.将其放在一个容器中(如列表、元组或字典)
引用计数减少的情况:
1.使用del语句对对象别名显示的销毁
2.引用超出作用域或被重新赋值
sys.getrefcount()函数可以获得对象的当前引用计数,多数情况下,引用计数比你猜测的要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。
(2)垃圾回收
1.当一个对象的引用计数归零时,它将被垃圾回收机制处理掉。
2.当两个对象a和b相互作用时,del语句可以减少a和b的引用计数,并销毁用于底层对象的名称。然而由于每个对象都包含一个对其他对象的引用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄漏)。为了解决这一个问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
(3)内存池机制
Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
1.Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池的机制,用于管理对小块内存的申请和释放。
2.Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。
3.对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象不共享它们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
2.什么是lambda函数?它有什么好处?
答:lambda表达式,通常是在需要一个函数,但是又不想去在一个命名函数的场合下使用,也就是指匿名函数。
lambda函数:首要用途是指短小的回调函数。
lambda[arguments]:expression
>» a=lambda x,y:x+y
>» a(3,11)
lambda是一个常被用于代码中的单个表达式的匿名函数。
匿名函数lambda没有语句的原因,是它被用于在代码被执行的时候构建新的函数对象并且返回。
3.Python里面怎么实现tuple和list的转换?
直接使用turple()和list()进行转换就行了,type()可以判断对象的类型
4.请写出一段Python代码实现删除一个list里面的重复元素?
答:
1.使用set函数,set(list)
2.使用字典函数,如:
>» a = [1,2,4,2,4,5,6,5,7,8,9,0]
>» b = {}
>» b = b.fromkeys(a)
>» b
{0: None, 1: None, 2: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}
>» c = list(b.keys())
>» c
[0, 1, 2, 4, 5, 6, 7, 8, 9]
5.编程用sort进行排序,然后从最后一个元素开始判断是否重复并去重
>» a = [1,2,4,2,4,5,7,10,5,5,7,8,9,0,3]
>» a.sort()
>» a
[0, 1, 2, 2, 3, 4, 4, 5, 5, 5, 7, 7, 8, 9, 10]
>» last = a[-1]
>» last
10
>» for i in range(len(a)-2,-1,-1):
if last == a[i]:
del a[i]
else:
last = a[i]
print(a)
[0, 1, 2, 2, 3, 4, 4, 5, 5, 5, 7, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 4, 5, 5, 5, 7, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 4, 5, 5, 5, 7, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 4, 5, 5, 5, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 4, 5, 5, 5, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 4, 5, 5, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 4, 5, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 4, 5, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 5, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 5, 7, 8, 9, 10]
[0, 1, 2, 2, 3, 4, 5, 7, 8, 9, 10]
[0, 1, 2, 3, 4, 5, 7, 8, 9, 10]
[0, 1, 2, 3, 4, 5, 7, 8, 9, 10]
[0, 1, 2, 3, 4, 5, 7, 8, 9, 10]
6.Python中如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)
答:
①赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个。
②浅拷贝:创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会修改改变){1,完全切片方法;2,工厂函数,如list();3,copy模块的copy()函数}
③深拷贝:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变){copy模块的deep.deepcopy()函数}
下面举例说明:
|
|
7.介绍一下except的用法和作用?
答:try…except…except…[else…][finally…]
执行try下的语句,如果引发异常,则执行过程会跳到except语句。对每个except分支顺序尝试执行,如果引发的异常与except中的异常组匹配,执行相应的语句。
如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。
try下的语句正常执行,则执行else块代码。如果发生异常,就不会执行
如果存在finally语句,最后总是会执行。
8.Python中pass语句的作用是什么?
答:pass语句不会执行任何操作,一般作为占位符或者创建占位程序,
while False:
pass
换一句话说,pass是一个在python中不会被执行的语句,在复杂的语句中,如果一个地方需要暂时被留白,它常常被用于占位符。
9.介绍一下Python下range()函数的用法,range和xrange的区别?
答:列出一组数据,经常用在for in range()循环中 。
range函数说明:range([start,]stop[,step]),根据start与stop指定的范围以及step设定的步长,生成一个序列。
range示例:
>» range(5)
[0, 1, 2, 3, 4]
>» range(1,5)
[1, 2, 3, 4]
>» range(0,6,2)
[0, 2, 4]
xrange函数说明:用法与range完全一样,所不同的是生成不是一个数组,而是一个生成器。
xrange示例:
>» xrange(5)
xrange(5)
>» list(xrange(5))
[0, 1, 2, 3, 4]
>» xrange(1,5)
xrange(1, 5)
>» list(xrange(1,5))
[1, 2, 3, 4]
>» list(xrange(0,6,2))
[0, 2, 4]
由以上的示例可以知道,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间,这两个基本上都是在循环的时候用:
>» for i in range(0,100):
print i,
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
>» for i in xrange(0,100):
print i,
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
>»
这两个输出的结果都是一样的,实际上有很多不同,range会直接生成一个list对象:
>» a = range(0,100)
>» print(type(a))
<type ’list'>
>» print a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>» print a[0],a[1]
0 1
而xrange则不会直接生成一个list,而是每次调用返回其中一个值:
>» a = xrange(0,100)
>» print(type(a))
<type ‘xrange’>
>» print a
xrange(100)
>» print a[0],a[1]
0 1
总结如下:
I.xrange用于返回一个range对象,而range用于返回一个数组。且不管那个范围有多大,xrange都使用相同的内存。
II.为什么xrange随着参数的规模增大对内存占用几乎没有影响。那么它和range的区别在哪里呢?
range返回的是列表,而xrange返回的是一个可迭代的对象,它给外部提供了一种遍历其内部元素,而不用关心其内部实现的方法。有一种自定义的zrange的实现是,最关键的实现是建立了一个内部指针__pointer, 它记录当前的访问的位置, 下次的访问就可以通过指针的状态进行相应的操作。 通过指针记录位置,下次访问把指针移动一个单位,而xrange是把所有的都放到内存中。
10.如何用Python来进行查询和替换一个文本字符串?
答:可以使用re模块的sub()函数和subn()函数来进行查询和替换。
格式:sub(replacement, string[,count=0])(replacement是被替换成的文本,string是需要被替换的文本,count是一个可选参数,指最大被替换的数量)
|
|
subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新字符串和总共替换的数量。
11.Python里面match()和search()的区别?
答:re模块中match(pattern,string[,flags]),检查string的开头是否与pattern匹配。
re模块中re.search(pattern,string[,flags]),,在string搜索pattern的第一个搜索值。
|
|
12.用python匹配HTML tag的时候,<.*>和<.*?>有什么区别?
答:术语叫贪婪匹配(<.*>)和非贪婪匹配(<.*?>)
例如:
test
<.*>:
test
<.*?>:
贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配。非贪婪模式只被部分NFA引擎所支持。
属于贪婪模式的量词,也叫做匹配优先量词,包括:
“{m,n}”、“{m,}”、“?”、“*”和“+”。
在一些使用NFA引擎的语言中,在匹配优先量词后加上“?”,即变成属于非贪婪模式的量词,也叫做忽略优先量词,包括: “{m,n}?”、“{m,}?”、“??”、“*?”和“+?”。
从正则语法的角度来讲,被匹配优先量词修饰的子表达式使用的就是贪婪模式,如“(Expression)+”;被忽略优先量词修饰的子表达式使用的就是非贪婪模式,如“(Expression)+?”。
13.Python里面如何生成随机数?
答:random模块。
随机整数:random.randint(a,b):返回整数x,a<=x<=b
random.randrange(start,stop,[,step]):返回一个范围在(start,stop,step)之间的随机整数,不包括结束值。
随机实数:random.random():返回0到1之间的浮点数
random.uniform(a,b):返回指定范围内的浮点数
14.有没有一个工具可以帮助查找python的bug和进行静态的代码分析?
答:PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告;Pylint是另外一个工具可以进行codingstandard检查。
15.如何在一个function里面设置一个全局的变量?
答:解决方法是在function的开始插入一个global声明:
def f():
global x
16.单引号,双引号,三引号的区别
答:单引号和双引号是等待的,如果要换行,需要符号(\),三引号则可以直接换行,并且可以包含注释。
如果要表示Let’ s go这个字符串
单引号:s4 = ‘Let\’ s go’
双引号:s5 = “Let’ s go”
s6 = ‘I realy like“python”!’
这就是单引号和双引号都可以表示字符串的原因了。
|
|
(2)Python常见面试题2
1.到底什么是Python,你可以在回答中与其他技术进行对比。
以下是一些关键点:
1)Python是一种解释性语言。这就是说,与C语言和C的派生语言不同,Python代码在运行之前不需要编译。其它解释型语言还包括PHP和Ruby。
2)Python是动态语言,指的是你在声明变量类型的时候,不需要说明变量的类型。你可以直接编写类似在x = 111和x = “I’m a string"这样的代码,程序不会报错。
3)Python非常适合面向对象的编程(OOP),因为它支持通过组合(composition)与继承(inheritance)的方式定义类(class)。Python没有访问说明符(access specifier,类似C++中的public和private),因为相信大家能处理好公有和私有。
4)在Python语言中,函数是第一类对象(first-class objects)。这指的是它们可以被指定给变量,函数既能返回函数类型,也可以接受函数作为输入。类(class)也是第一类对象。
5)Python代码编写快,但是运行速度比编译语言通常要慢。好在Python允许加入基于C语言编写的扩展,因此我们能够优化代码,消除瓶颈,这点通常是可以实现的。numpy就是一个很好地例子,它的运行速度真的非常快,因为很多算术运算其实并不是通过Python实现的。
-
Python用途非常广泛——网络应用,自动化,科学建模,大数据应用,等等。它也常被用作“胶水语言”,帮助其他语言和组件改善运行状况。
-
Python让困难的事情变得容易,因此程序员可以专注于算法和数据结构的设计,而不用处理底层的细节。
2.什么是PEP8?
PEP8是一个编程规范,内容是一些关于如何让你的程序变得更具可读性的建议。
3.什么是picking和unpicking?
Pickle模块读入任何Python对象,将它们转换成字符串,然后使用dump函数将其转储到一个文件中——这个过程叫做pickling。
反之从存储的字符串文件中提取原始Python对象的过程,叫做unpickling。
4.Python是如何被解释的?
Python是一种解释性语言,它的源代码可以直接运行。Python解释器会将源代码转换成中间语言,之后再翻译成机器码再执行。
5.Python是怎样管理内存的?
①Python的内存管理是由私有heap空间管理的。所有的Python对象和数据结构都在一个私有heap中。程序员没有访问该heap的权限,只有解释器才能对它进行操作。
②为Python的heap空间分配内存是由Python的内存管理模块进行的,其核心API会提供一些访问该模块的方法供程序员使用。
③Python有自带的垃圾回收系统,它回收并释放没有被使用的内存,让它们能够被其他程序使用。
6.什么是Python装饰器?
Python装饰器是Python中的特有变动,可以使修改函数变得容易。
7.数组和元组之间的区别是什么?
数组和元组之间的区别:数组内容是可以被修改的,而元组内容是只读的。另外,元组可以被哈希,比如作为字典的关键字。
8.参数按值传递和引用传递是怎样实现的?
Python中的一切都是类,所有的变量都是一个对象的引用。引用的值是由函数确定的,因此无法被改变。但是如果一个对象是可以被修改的,你可以改动对象。
9.字典推导式和列表推导式是什么?
它们是可以轻松创建字典和列表的语法结构。
10.Python都有哪些自带的数据结构?
Python自带的数据结构分为可变的和不可变的。可变的有:数组、集合、字典;不可变的有:字符串、元组、数。
底层实现?
11.什么是Python的命名空间?
在Python中,所有的名字都存在于一个空间中,它们在该空间中存在和被操作——这就是命名空间。
它就好像一个盒子,每一个变量名字都对应装着一个对象。当查询变量的时候,会从该盒子里面寻找相应的对象。
12.Python中什么是遍历器?
遍历器用于遍历一组元素,比如列表这样的容器。
13.Python中unittest是什么?
在Python中,unittest是Python中的单元测试框架。它拥有支持共享搭建、自动测试、在测试中暂停代码、将不同测试迭代成一组,等等的功能。
**Slicing(切片)**是一种在有序的对象类型中(数组,元组,字符串)截选某一段的语法。
生成器是实现迭代器的一种机制。它功能的实现依赖于yield表达式,除此之外和普通的函数没有什么区别。Python中不同于列表按某种算法推算出来,可以在循环的过程中不断推算出后续元素。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。(这样不必创建完整的list,从而节省大量的空间)
Python中文档字符串被称为docstring,它在Python中的作用是为函数、模块和类注释生成文档。
14.Python中的负索引是什么?
Python中的序列索引可以是正也可以是负。如果是正索引,0是序列中的第一个索引,1是第二个索引。
如果是负索引,(-1)是最后一个索引(-2)是倒数第二个索引。
15.如何将一个数字转换成一个字符串?
可以使用自带函数str()将一个数字转换为字符串。如果你想要八进制或十六进制,可以用oct()或hex()。
16.Python中的模块和包是什么?
在Python中,模块是搭建程序的一种方式。每一个Python代码文件都是一个模块,并可以引用其他的模块,比如对象和属性。
一个包含许多Python代码的文件夹是一个包。一个包可以包含模块和子文件夹。
(3)Python常见面试题3
1.补充缺失的代码
|
|
|
|
特别要注意以下几个点:
- 命名规范要统一。如果样本代码中能够看出命名规范,遵循其已有的规范。
- 递归函数需要递归并终止。确保你明白其中的原理,否则你将面临无休无止的调用栈(callstack)。
- 使用
os
模块与操作系统进行交互,同时做到交互方式是可以跨平台的。你可以把代码写成sChildPath = sPath + '/' + sChild
,但是这个在Windows系统上会出错。 - 熟悉基础模块是非常有价值的,但不可能都记下来,不清楚可以baidu或google。
- 如果对代码的预期功能不明,就大胆的提问。
- 坚持KISS原则!保持简单,不过脑子就能懂!(保持简单易懂)
2.阅读下列代码,写出A0,A1至An的最终值
|
|
答案:
A0 = {‘a’: 1, ‘c’: 3, ‘b’: 2, ’e’: 5, ’d’: 4}
A1 = [0,1,2,3,4,5,6,7,8,9]
A2 = []
A3 = [1,3,2,5,4]
A4 = [1,2,3,4,5]
A5 = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
A6 = [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]]
3.Python和多线程(multi-threading),列举一下让Python代码以并行方式运行的方法。
答案:
Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是一个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常快,会让你误以为线程是并行执行的,但是实际上都是轮流执行的。经过GIL这一道关卡处理,会增加执行的开销。这也就意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的办法。
不过还是有很多理由促使我们使用threading包的。如果你想同时执行一些任务,而且不考虑效率问题,那么使用这些包是没有什么问题的。但是大部分情况下,并不是这么一回事,你会希望把多线程的部分外包给操作系统完成(通过开启多个进程),或者是某些调用你的Python代码的外部程序(例如Spark或Hadoop),又或是你的Python代码调用的其它代码(例如,你可以在Python中调用C函数,用于处理开销较大的多线程工作)。
4.你如何管理不同版本的代码?
版本管理。我们更倾向于使用Git作为版本控制系统(VCS),但还有其它的选择,比如subversion(SVN)。
为什么要有版本控制呢?
因为没有版本控制的代码,就像没有杯子的coffee。有时候我们需要写一些一次性的、可以随手扔掉的脚本,这种情况下不作版本控制没关系。但是面对的是大量的代码,使用版本控制系统是有利的。版本控制能够帮你追踪谁对代码库做了什么操作;发现新引入的什么bug;管理你的软件的不同版本和发行版;在团队成员中分享源代码;部署及其他自动化处理。它能让你回滚到出现问题之前的版本,还有很多优良版本的控制功能。
5.下面代码会输出什么?
|
|
答案:
>» f(2)
[0, 1]
>» f(3,[3,2,1])
[3, 2, 1, 0, 1, 4]
>» f(3)
[0, 1, 0, 1, 4]
(1)第一个函数调用十分明显,for循环先后将0和1添加到了空列表中。l是变量的一个名字,指向内存中存储中的一个列表。
(2)第二个函数调用在一块新的内存中创建了新的列表。l这时指向了新生成的列表。之后再往新列表中添加0,1,4.
(3)第三个函数调用有些奇怪,它使用了之前内存地址中存储的旧列表。这就是为什么它的前两个元素是0和1了。
改写成下面的代码运行下或许尝试着理解哈:
|
|
6.“猴子补丁”(monkey patching)指的是什么?这种做法好吗?
答案:
“猴子补丁"就是指,在函数或对象已经定义之后,再去改变它们的行为。
这种说法起源于Zope框架,大家在修正Zope的Bug的时候经常在程序后面追加更新部分,这些被称作是"杂牌军补丁(guerilla patch)",后来guerilla就渐渐的写成了gorllia(猩猩),再后来就写成了monkey(猴子),所以猴子补丁的叫法就是这么莫名其妙的得来的。
猴子补丁主要有以下几个用处:
1.在运行时替换方法、属性等
2.在不修改第三方代码的情况下增加原来不支持的功能
3.在运行时为内存中的对象增加patch而不是在磁盘的源代码中增加
举一比较实用的例子:很多代码用到import json,后来发现ujson性能更高,如果觉得把每个文件的import json改换成import ujson as json的成本较高,或者说想测试一下用ujson替换json是否符合预期,只需要在入口加上:
|
|
|
|
运行main.py可以看到的都是输出’ujson’,说明后面impor的json是被patch了的。
只需要将以上的一个patch写在一个文件里自己再import一次,便可实现自己想要的功能,这是非常方便的。
可以知道猴子补丁的主要功能便是在不去改变源码的情况下而对功能进行追加和变更;对于编程过程中使用一些第三方的需求的情况下,使用猴子补丁都是十分方便的。
原因:问猴子补丁的问题是看看你是否对单元测试的方法有一定的了解。
*7.这两个参数是什么意思:args,**kwargs?
答案:
如果我们不确定要往函数中传入几个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使用*args;
如果我们不知道要往函数中传入多少个关键词参数时,或者想传入字典的值作为关键词参数时,那就使用**kwargs。
args和kwargs这两个标识是约定俗成的用法。下面给出具体的示例;
x.python2和python3的差别在哪里?(你自己举例子,尽量多提几点)
(来自ThoughtsWork)