《python面试题精选.doc》由会员分享,可在线阅读,更多相关《python面试题精选.doc(44页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、-作者xxxx-日期xxxxpython面试题精选【精品文档】问题1到底什么是Python?你可以在回答中与其他技术进行对比(也鼓励这样做)。答案下面是一些关键点: Python是一种解释型语言,Python代码在运行之前不需要编译。 Python是动态类型语言,在声明变量时,不需要说明变量的类型。 Python非常适合面向对象的编程(OOP),因为它支持通过组合(composition)与继承(inheritance)的方式定义类(class)。Python中没有访问说明符public和private, 在Python语言中,函数是第一类对象(first-class objects)。这指的
2、是它们可以被指定给变量,函数既能返回函数类型,也可以接受函数作为输入。类(class)也是第一类对象。 Python代码编写快,但是运行速度比编译语言通常要慢。ython允许加入基于C语言编写的扩展,因此我们能够优化代码,消除瓶颈,这点通常是可以实现的。numpy就是一个很好地例子,它的运行速度真的非常快,因为很多算术运算其实并不是通过Python实现的。 Python用途非常广泛网络应用,自动化,科学建模,大数据应用,等等。它也常被用作“胶水语言”,帮助其他语言和组件改善运行状况。 Python让困难的事情变得容易,因此程序员可以专注于算法和数据结构的设计,而不用处理底层的细节。问题2补充缺
3、失的代码def print_directory_contents(sPath): 这个函数接受文件夹的名称作为输入参数, 返回该文件夹中文件的路径, 以及其包含文件夹中文件的路径。 # 补充代码答案def print_directory_contents(sPath): import os for sChild in os.listdir(sPath): sChildPath = os.path.join(sPath,sChild) if os.path.isdir(sChildPath): print_directory_contents(sChildPath) else: print sC
4、hildPath特别要注意以下几点: 命名规范要统一。如果样本代码中能够看出命名规范,遵循其已有的规范。 递归函数需要递归并终止。确保你明白其中的原理,否则你将面临无休无止的调用栈(callstack)。 我们使用os模块与操作系统进行交互,同时做到交互方式是可以跨平台的。你可以把代码写成sChildPath = sPath + / + sChild,但是这个在Windows系统上会出错。 熟悉基础模块是非常有价值的,但是别想破脑袋都背下来,记住Google是你工作中的良师益友。 如果你不明白代码的预期功能,就大胆提问。 坚持KISS原则!保持简单,不过脑子就能懂!为什么提这个问题: 说明面试
5、者对与操作系统交互的基础知识 递归真是太好用啦问题3阅读下面的代码,写出A0,A1至An的最终值。A0 = dict(zip(a,b,c,d,e),(1,2,3,4,5)A1 = range(10)A2 = i for i in A1 if i in A0A3 = A0s for s in A0A4 = i for i in A1 if i in A3A5 = i:i*i for i in A1A6 = i,i*i for i in A1答案A0 = a: 1, c: 3, b: 2, e: 5, d: 4A1 = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9A2 = A3 = 1
6、, 3, 2, 5, 4A4 = 1, 2, 3, 4, 5A5 = 0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81A6 = 0, 0, 1, 1, 2, 4, 3, 9, 4, 16, 5, 25, 6, 36, 7, 49, 8, 64, 9, 81问题4Python和多线程(multi-threading)。这是个好主意码?列举一些让Python代码以并行方式运行的方法。答案Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意
7、。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的方法。不过还是有很多理由促使我们使用threading包的。如果你想同时执行一些任务,而且不考虑效率问题,那么使用这个包是完全没问题的,而且也很方便。但是大部分情况下,并不是这么一回事,你会希望把多线程的部分外包给操作系统完成(通过开启多个进程),或者是某些调
8、用你的Python代码的外部程序(例如Spark或Hadoop),又或者是你的Python代码调用的其他代码(例如,你可以在Python中调用C函数,用于处理开销较大的多线程工作)。问题5你如何管理不同版本的代码?答案:版本管理!GIT, SVN问题6下面代码会输出什么:def f(x,l=): for i in range(x): l.append(i*i) print lf(2)f(3,3,2,1)f(3)答案:0, 13, 2, 1, 0, 1, 40, 1, 0, 1, 4呃?第一个函数调用十分明显,for循环先后将0和1添加至了空列表l中。l是变量的名字,指向内存中存储的一个列表。第
9、二个函数调用在一块新的内存中创建了新的列表。l这时指向了新生成的列表。之后再往新列表中添加0、1、2和4。很棒吧。第三个函数调用的结果就有些奇怪了。它使用了之前内存地址中存储的旧列表。这就是为什么它的前两个元素是0和1了。不明白的话就试着运行下面的代码吧:l_mem = l = l_mem # the first callfor i in range(2): l.append(i*i)print l # 0, 1l = 3,2,1 # the second callfor i in range(3): l.append(i*i)print l # 3, 2, 1, 0, 1, 4l = l_m
10、em # the third callfor i in range(3): l.append(i*i)print l # 0, 1, 0, 1, 4问题7monkey patch (猴子补丁)用来在运行时动态修改已有的代码,而不需要修改原始代码。简单的monkey patch 实现:python#coding=utf-8def originalFunc(): print this is original function!def modifiedFunc(): modifiedFunc=1 print this is modified function!def main(): original
11、Func()if _name_=_main_: originalFunc=modifiedFunc main()python中所有的东西都是object,包括基本类型。查看一个object的所有属性的方法是:dir(obj)函数在python中可以像使用变量一样对它进行赋值等操作。查看属性的方法:print locals()print globals()问题8这两个参数是什么意思:*args,*kwargs?我们为什么要使用它们?答案如果我们不确定要往函数中传入多少个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使要用*args;如果我们不知道要往函数中传入多少个关键词参数,或者想传
12、入字典的值作为关键词参数时,那就要使用*kwargs。args和kwargs这两个标识符是约定俗成的用法,你当然还可以用*bob和*billy,但是这样就并不太妥。下面是具体的示例:def f(*args,*kwargs): print args, kwargsl = 1,2,3t = (4,5,6)d = a:7,b:8,c:9f()f(1,2,3) # (1, 2, 3) f(1,2,3,groovy) # (1, 2, 3, groovy) f(a=1,b=2,c=3) # () a: 1, c: 3, b: 2f(a=1,b=2,c=3,zzz=hi) # () a: 1, c: 3,
13、 b: 2, zzz: hif(1,2,3,a=1,b=2,c=3) # (1, 2, 3) a: 1, c: 3, b: 2f(*l,*d) # (1, 2, 3) a: 7, c: 9, b: 8f(*t,*d) # (4, 5, 6) a: 7, c: 9, b: 8f(1,2,*t) # (1, 2, 4, 5, 6) f(q=winning,*d) # () a: 7, q: winning, c: 9, b: 8f(1,2,*t,q=winning,*d) # (1, 2, 4, 5, 6) a: 7, q: winning, c: 9, b: 8def f2(arg1,arg2,
14、*args,*kwargs): print arg1,arg2, args, kwargsf2(1,2,3) # 1 2 (3,) f2(1,2,3,groovy) # 1 2 (3, groovy) f2(arg1=1,arg2=2,c=3) # 1 2 () c: 3f2(arg1=1,arg2=2,c=3,zzz=hi) # 1 2 () c: 3, zzz: hif2(1,2,3,a=1,b=2,c=3) # 1 2 (3,) a: 1, c: 3, b: 2f2(*l,*d) # 1 2 (3,) a: 7, c: 9, b: 8f2(*t,*d) # 4 5 (6,) a: 7,
15、c: 9, b: 8f2(1,2,*t) # 1 2 (4, 5, 6) f2(1,1,q=winning,*d) # 1 1 () a: 7, q: winning, c: 9, b: 8f2(1,2,*t,q=winning,*d) # 1 2 (4, 5, 6) a: 7, q: winning, c: 9, b: 8为什么提这个问题?有时候,我们需要往函数中传入未知个数的参数或关键词参数。有时候,我们也希望把参数或关键词参数储存起来,以备以后使用。有时候,仅仅是为了节省时间。问题9下面这些是什么意思:classmethod,staticmethod,property?回答背景知识这些都
16、是装饰器(decorator)。装饰器是一种特殊的函数,要么接受函数作为输入参数,并返回一个函数,要么接受一个类作为输入参数,并返回一个类。标记是语法糖(syntactic sugar),可以让你以简单易读得方式装饰目标对象。my_decoratordef my_func(stuff): do_thingsIs equivalent todef my_func(stuff): do_thingsmy_func = my_decorator(my_func)你可以在本网站上找到介绍装饰器工作原理的教材。真正的答案classmethod,staticmethod和property这三个装饰器的使用
17、对象是在类中定义的函数。下面的例子展示了它们的用法和行为:class MyClass(object): def _init_(self): self._some_property = properties are nice self._some_other_property = VERY nice def normal_method(*args,*kwargs): print calling normal_method(0,1).format(args,kwargs) classmethod def class_method(*args,*kwargs): print calling clas
18、s_method(0,1).format(args,kwargs) staticmethod def static_method(*args,*kwargs): print calling static_method(0,1).format(args,kwargs) property def some_property(self,*args,*kwargs): print calling some_property getter(0,1,2).format(self,args,kwargs) def some_property(self,*args,*kwargs): print callin
19、g some_property setter(0,1,2).format(self,args,kwargs) self._some_property = args0 property def some_other_property(self,*args,*kwargs): print calling some_other_property getter(0,1,2).format(self,args,kwargs)o = MyClass()# 未装饰的方法还是正常的行为方式,需要当前的类实例(self)作为第一个参数。o.normal_method # bound method MyClass
20、.normal_method of o.normal_method() # normal_method(,),)o.normal_method(1,2,x=3,y=4) # normal_method(, 1, 2),y: 4, x: 3)# 类方法的第一个参数永远是该类# bound method classobj.class_method of o.class_method()# class_method(,),)o.class_method(1,2,x=3,y=4)# class_method(, 1, 2),y: 4, x: 3)# 静态方法(static method)中除了你调用时
21、传入的参数以外,没有其他的参数。# o.static_method()# static_method(),)o.static_method(1,2,x=3,y=4)# static_method(1, 2),y: 4, x: 3)# property是实现getter和setter方法的一种方式。直接调用它们是错误的。# “只读”属性可以通过只定义getter方法,不定义setter方法实现。# 调用some_property的getter(,(),)# properties are nice# “属性”是很好的功能o.some_property()# calling some_propert
22、y getter(,(),)# Traceback (most recent call last):# File , line 1, in # TypeError: str object is not callable# calling some_other_property getter(,(),)# VERY nice# o.some_other_property()# calling some_other_property getter(,(),)# Traceback (most recent call last):# File , line 1, in # TypeError: st
23、r object is not callableo.some_property = groovy# calling some_property setter(,(groovy,),)# calling some_property getter(,(),)# groovyo.some_other_property = very groovy# Traceback (most recent call last):# File , line 1, in # AttributeError: cant set attribute# calling some_other_property getter(,
24、(),)问题10阅读下面的代码,它的输出结果是什么?class A(object): def go(self): print go A go! def stop(self): print stop A stop! def pause(self): raise Exception(Not Implemented)class B(A): def go(self): super(B, self).go() print go B go!class C(A): def go(self): super(C, self).go() print go C go! def stop(self): super(C
25、, self).stop() print stop C stop!class D(B,C): def go(self): super(D, self).go() print go D go! def stop(self): super(D, self).stop() print stop D stop! def pause(self): print wait D wait!class E(B,C): passa = A()b = B()c = C()d = D()e = E()# 说明下列代码的输出结果a.go()b.go()c.go()d.go()e.go()a.stop()b.stop()
26、c.stop()d.stop()e.stop()a.pause()b.pause()c.pause()d.pause()e.pause()答案输出结果以注释的形式表示:a.go()# go A go!b.go()# go A go!# go B go!c.go()# go A go!# go C go!d.go()# go A go!# go C go!# go B go!# go D go!e.go()# go A go!# go C go!# go B go!a.stop()# stop A stop!b.stop()# stop A stop!c.stop()# stop A stop!
27、# stop C stop!d.stop()# stop A stop!# stop C stop!# stop D stop!e.stop()# stop A stop!a.pause()# . Exception: Not Implementedb.pause()# . Exception: Not Implementedc.pause()# . Exception: Not Implementedd.pause()# wait D wait!e.pause()# .Exception: Not Implemented问题11阅读下面的代码,它的输出结果是什么?class Node(obj
28、ect): def _init_(self,sName): self._lChildren = self.sName = sName def _repr_(self): return .format(self.sName) def append(self,*args,*kwargs): self._lChildren.append(*args,*kwargs) def print_all_1(self): print self for oChild in self._lChildren: oChild.print_all_1() def print_all_2(self): def gen(o
29、): lAll = o, while lAll: oNext = lAll.pop(0) lAll.extend(oNext._lChildren) yield oNext for oNode in gen(self): print oNodeoRoot = Node(root)oChild1 = Node(child1)oChild2 = Node(child2)oChild3 = Node(child3)oChild4 = Node(child4)oChild5 = Node(child5)oChild6 = Node(child6)oChild7 = Node(child7)oChild
30、8 = Node(child8)oChild9 = Node(child9)oChild10 = Node(child10)oRoot.append(oChild1)oRoot.append(oChild2)oRoot.append(oChild3)oChild1.append(oChild4)oChild1.append(oChild5)oChild2.append(oChild6)oChild4.append(oChild7)oChild3.append(oChild8)oChild3.append(oChild9)oChild6.append(oChild10)# 说明下面代码的输出结果
31、oRoot.print_all_1()oRoot.print_all_2()答案oRoot.print_all_1()会打印下面的结果:oRoot.print_all_1()会打印下面的结果:为什么提这个问题?因为对象的精髓就在于组合(composition)与对象构造(object construction)。对象需要有组合成分构成,而且得以某种方式初始化。这里也涉及到递归和生成器(generator)的使用。生成器是很棒的数据类型。你可以只通过构造一个很长的列表,然后打印列表的内容,就可以取得与print_all_2类似的功能。生成器还有一个好处,就是不用占据很多内存。有一点还值得指出,就
32、是print_all_1会以深度优先(depth-first)的方式遍历树(tree),而print_all_2则是宽度优先(width-first)。有时候,一种遍历方式比另一种更合适。但这要看你的应用的具体情况。问题12简要描述Python的垃圾回收机制(garbage collection)。答案这里能说的很多。你应该提到下面几个主要的点: Python在内存中存储了每个对象的引用计数(reference count)。如果计数值变成0,那么相应的对象就会消失,分配给该对象的内存就会释放出来用作他用。 偶尔也会出现引用循环(reference cycle)。垃圾回收器会定时寻找这个循环,
33、并将其回收。举个例子,假设有两个对象o1和o2,而且符合o1.x = o2和o2.x = o1这两个条件。如果o1和o2没有其他代码引用,那么它们就不应该继续存在。但它们的引用计数都是1。 Python中使用了某些启发式算法(heuristics)来加速垃圾回收。例如,越晚创建的对象更有可能被回收。对象被创建之后,垃圾回收器会分配它们所属的代(generation)。每个对象都会被分配一个代,而被分配更年轻代的对象是优先被处理的。问题13将下面的函数按照执行效率高低排序。它们都接受由0至1之间的数字构成的列表作为输入。这个列表可以很长。一个输入列表的示例如下:random.random() f
34、or i in range(100000)。你如何证明自己的答案是正确的。def f1(lIn): l1 = sorted(lIn) l2 = i for i in l1 if i0.5 return i*i for i in l2def f2(lIn): l1 = i for i in lIn if i0.5 l2 = sorted(l1) return i*i for i in l2def f3(lIn): l1 = i*i for i in lIn l2 = sorted(l1) return i for i in l1 if i(0.5*0.5)答案按执行效率从高到低排列:f2、f1和
35、f3。要证明这个答案是对的,你应该知道如何分析自己代码的性能。Python中有一个很好的程序分析包,可以满足这个需求。import cProfilelIn = random.random() for i in range(100000)cProfile.run(f1(lIn)cProfile.run(f2(lIn)cProfile.run(f3(lIn)14、getattr、setattr、hasattr的功能自省(让对象告诉我们他是什么),用于实现在运行时获取未知对象的信息。访问对象属性:hasattr(object, name)判断一个对象里面是否有name属性或者name方法,返回BOO
36、L值,有name特性返回True, 否则返回False。需要注意的是name要用括号括起来getattr(object, name,default)获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选。需要注意的是,如果是返回的对象的方法,返回的是方法的内存地址,如果需要运行这个方法,可以在后面添加一对括号。setattr(object, name, values)给对象的属性赋值,若属性不存在,先创建再赋值。15、staticmethod和classmethodpython有3个方法,即静态方法(staticmethod),类方法(classmethod
37、)和实例方法。def foo(x): print executing foo(%s)%(x)class A(object): def foo(self,x): print executing foo(%s,%s)%(self,x) classmethod def class_foo(cls,x): print executing class_foo(%s,%s)%(cls,x) staticmethod def static_foo(x): print executing static_foo(%s)%xa=A()这个self和cls是对类或者实例的绑定,对于一般的函数来说我们可以这么调用foo(x),这个函数就是最常用的,它的工作跟任何东西(类,实例)无关.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self, x),为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a, x).类方法一样,只不过它传递的是类而不是实例,A.class_foo(x).注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好.对于静态方法其实和普通的方法一样,
限制150内