python最新面试题整理(二)

1 Python的函数参数传递

看两个如下例子,分析运行结果: 代码一:

a = 1 def fun(a): a = 2 fun(a) print(a) # 1

代码二:

a = [] def fun(a): a.append(1) fun(a) print(a) # [1]

所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。 这里记住的是类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。(这就是这个问题的重点) 当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.

2 Python中的元类(metaclass)

元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,但是我们已经学习到了Python中的类也是对象。好吧,元类就是用来创建这些类(对象)的,元类就是类的类 这个非常的不常用,详情请看:《深刻理解Python中的元类(metaclass)》

3 @staticmethod和@classmethod

Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下:

class A(object): def foo(self,x): print "executing foo(%s,%s)"%(self,x)

<code>@classmethoddef class_foo(cls,x):    print( "executing class_foo(%s,%s)"%(cls,x))@staticmethoddef static_foo(x):    print ("executing static_foo(%s)"%x)/<code>

a=A() 这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self, x),为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a, x)).类方法一样,只不过它传递的是类而不是实例,A.class_foo(x).注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好. 对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用a.static_foo(x)或者A.static_foo(x)来调用. \\ 实例方法 类方法 静态方法 a = A() a.foo(x) a.class_foo(x) a.static_foo(x) A 不可用 A.class_foo(x) A.static_foo(x)

4 类变量和实例变量

<code>class Person:name="aaa"/<code>

p1=Person() p2=Person() p1.name="bbb" print(p1.name) # bbb print(p2.name) # aaa print(Person.name) # aaa 类变量就是供类使用的变量,实例变量就是供实例使用的. 这里p1.name="bbb"是实例调用了类变量,这其实和上面第一个问题一样,就是函数传参的问题,p1.name一开始是指向的类变量name="aaa",但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了. 可以看看下面的例子:

<code>class Person:name=[]/<code>

p1=Person() p2=Person() p1.name.append(1) print(p1.name) # [1] print(p2.name) # [1] print(Person.name) # [1]

5 Python自省

这个也是python彪悍的特性. 自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance().

6 字典推导式

可能你见过列表推导时,却没有见过字典推导式,在2.7中才加入的: d = {key: value for (key, value) in iterable}

7 Python中单下划线和双下划线

<code>1>>> class MyClass():2 ...     def __init__(self):3 ...             self.__superprivate = "Hello"4 ...             self._semiprivate = ", world!"5 ...6 >>> mc = MyClass()7 >>> print(mc.__superprivate)8 Traceback (most recent call last):9  File "<stdin>", line 1, in <module>10 AttributeError: myClass instance has no attribute '__superprivate'11 >>> print(mc._semiprivate)12 , world!13 >>> print mc.__dict__14 {'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}/<module>/<stdin>/<code>

foo:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突. -foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式. -foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名. 详情见: http://www.zhihu.com/question/19754941

8 字符串格式化:%和.format

.format在许多方面看起来更便利.对于%最烦人的是它无法同时传递一个变量和元组.你可能会想下面的代码不会有什么问题: Python: "hi there %s" % name 但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做: "hi there %s" % (name,) # 提供一个单元素的数组而不是一个参数

9 迭代器和生成器

在Python中,这种一边循环一边计算的机制,称为生成器:generator。 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

这个是stackoverflow里python排名第一的问题,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python

10 * args and ** kwargs

用args和**kwargs只是为了方便并没有强制使用它们. 当你不确定你的函数里将要传递多少参数时你可以用args.例如,它可以传递任意数量的参数:

<code>1>>> def print_everything(*args):2     for count, thing in enumerate(args):3 ...       print '{0}. {1}'.format(count, thing)4 ..5 >>> print_everything('apple', 'banana', 'cabbage')6 0. apple7 1. banana8 2. cabbage/<code>

相似的 ** kwargs允许你使用没有事先定义的参数名:

<code>1>>> def table_things(**kwargs):2 ...     for name, value in kwargs.items():3 ...         print '{0} = {1}'.format(name, value)4 ...5 >>> table_things(apple = 'fruit', cabbage = 'vegetable')6 cabbage = vegetable7 apple = fruit/<code>

你也可以混着用.命名参数首先获得参数值然后所有的其他参数都传递给*args和**kwargs.命名参数在列表的最前端.例如:

<code>1def table_things(titlestring, ** kwargs)/<code>
  • args和** kwargs可以同时在函数的定义中,但是args必须在**kwargs前面. 当调用函数时你也可以用和**语法.例如:
<code>1>>> def print_three_things(a, b, c):2 ...     print 'a = {0}, b = {1}, c = {2}'.format(a,b,c)3 ...4 >>> mylist = ['aardvark', 'baboon', 'cat']5 >>> print_three_things(*mylist)67 a = aardvark, b = baboon, c = cat/<code>

就像你看到的一样,它可以传递列表(或者元组)的每一项并把它们解包.注意必须与它们在函数里的参数相吻合.当然,你也可以在函数定义或者函数调用时用*. http://stackoverflow.com/questions/3394835/args-and-kwargs

11 面向切面编程AOP和装饰器

这个AOP一听起来有点懵,同学面试的时候就被问懵了… 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。 这个问题比较大,推荐: http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python 中文: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/3/README.html

12 鸭子类型

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。 比如在python中,有很多file-like的东西,比如StringIO,GzipFile,socket。它们有很多相同的方法,我们把它们当作文件使用。 又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器等. 鸭子类型在动态语言中经常使用,非常灵活,使得python不想java那样专门去弄一大堆的设计模式。

13 Python中重载

引自知乎:http://www.zhihu.com/question/20053359 函数重载主要是为了解决两个问题。

  1. 可变参数类型。
  2. 可变参数个数。 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。 好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。 那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。 好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。

14 新式类和旧式类

这个面试官问了,我说了老半天,不知道他问的真正意图是什么. 这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html 新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类是广度优先,旧式类是深度优先),<python>里讲的也很多./<python>

15 __new__和__init__的区别;

<code>这个__new__确实很少见到,先做了解吧.1.__new__是一个静态方法,而__init__是一个实例方法.2.__new__方法会返回一个创建的实例,而__init__什么都不返回.3.只有在__new__返回一个cls的实例时后面的__init__才能被调用.4.当创建一个新实例时调用__new__,初始化一个实例时用__init__.ps: __metaclass__是创建类时起作用.所以我们可以分别使用__metaclass__,__new__和__init__来分别在类创建,实例创建和实例初始化的时候做一些小手脚./<code>

16 单例模式

这个绝对常考啊.绝对要记住1~2个方法,当时面试官是让手写的.

1 使用__new__方法
<code>class Singleton(object):    def __new__(cls, *args, **kw):        if not hasattr(cls, '_instance'):            orig = super(Singleton, cls)            cls._instance = orig.__new__(cls, *args, **kw)        return cls._instanceclass MyClass(Singleton):    a = 1/<code>
2 共享属性

创建实例时把所有实例的__dict__指向同一个字典,这样它们具有相同的属性和方法.

<code>1class Borg(object):2    _state = {}3    def __new__(cls, *args, **kw):4        ob = super(Borg, cls).__new__(cls, *args, **kw)5        ob.__dict__ = cls._state6      return ob78 class MyClass2(Borg):9    a = 1/<code>
3 装饰器版本
<code>1def singleton(cls, *args, **kw):2    instances = {}3  def getinstance():4        if cls not in instances:5            instances[cls] = cls(*args, **kw)6        return instances[cls]7    return getinstance89 @singleton10 class MyClass:/<code>
4 import方法

作为python的模块是天然的单例模式

<code># mysingleton.pyclass My_Singleton(object):    def foo(self):        passmy_singleton = My_Singleton()# to usefrom mysingleton import my_singletonmy_singleton.foo()/<code> 

17 Python中的作用域

Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。 当 Python 遇到一个变量的话他会按照这样的顺序进行搜索: 本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)

18 GIL线程全局锁

线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程. 解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).

19 协程

简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态. Python里最常见的yield就是协程的思想!可以查看第九个问题.

20 闭包

闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。 当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点:

  1. 必须有一个内嵌函数
  2. 内嵌函数必须引用外部函数中的变量
  3. 外部函数的返回值必须是内嵌函数 感觉闭包还是有难度的,几句话是说不明白的,还是查查相关资料. 重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上. 闭包就像个空心球一样,你知道外面和里面,但你不知道中间是什么样.

21 lambda函数

其实就是一个匿名函数,为什么叫lambda?因为和后面的函数式编程有关.

22 Python函数式编程

这个需要适当的了解一下吧,毕竟函数式编程在Python中也做了引用. python中函数式编程支持: filter 函数的功能相当于过滤器。调用一个布尔函数bool_func来迭代遍历每个seq中的元素;返回一个使bool_seq返回值为true的元素的序列。

<code>>>>a = [1,2,3,4,5,6,7]>>>b = filter(lambda x: x > 5, a)>>>print b>>>[6,7]map函数是对一个序列的每个项依次执行函数,下面是对一个序列每个项都乘以2:>>> a = map(lambda x:x*2,[1,2,3])>>> list(a)[2, 4, 6]reduce函数是对一个序列的每个项迭代调用函数,下面是求3的阶乘:>>> reduce(lambda x,y:x*y,range(1,4))6/<code> 

23 Python里的拷贝

引用和copy(),deepcopy()的区别

<code>import copya = [1, 2, 3, 4, ['a', 'b']]  #原始对象b = a  #赋值,传对象的引用c = copy.copy(a)  #对象拷贝,浅拷贝d = copy.deepcopy(a)  #对象拷贝,深拷贝a.append(5)  #修改对象aa[4].append('c')  #修改对象a中的['a', 'b']数组对象print 'a = ', aprint 'b = ', bprint 'c = ', cprint 'd = ', d输出结果:a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]c =  [1, 2, 3, 4, ['a', 'b', 'c']]d =  [1, 2, 3, 4, ['a', 'b']]/<code>

24 Python垃圾回收机制

Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。

1 引用计数

PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。 优点:

  1. 简单
  2. 实时性 缺点:
  3. 维护引用计数消耗资源
  4. 循环引用
2 标记-清除机制

基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。

3 分代技术

分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。 Python默认定义了三代对象集合,索引数越大,对象存活时间越长。 举例: 当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。

25 Python里面如何实现tuple和list的转换?

答:tuple,可以说是不可变的list,访问方式还是通过索引下标的方式。 当你明确定义个tuple是,如果仅有一个元素,必须带有,例如:(1,)。 当然,在2.7以后的版,python里还增加了命名式的tuple!

至于有什么用,首先第一点,楼主玩过python都知道,python的函数可以有多返回值的,而python里,多返回值,就是用tuple来表示,这是用的最广的了,

比如说,你需要定义一个常量的列表,但你又不想使用list,那也可以是要你管tuple,例如: if a in ('A','B','C'):pass

26 Python的is

is是对比地址,==是对比值

27 read,readline和readlines

• read 读取整个文件 • readline 读取下一行,使用生成器方法 • readlines 读取整个文件到一个迭代器以供我们遍历

28 Python2和3的区别

大部分Python库都同时支持Python 2.7.x和3.x版本的,所以不论选择哪个版本都是可以的。但为了在使用Python时避开某些版本中一些常见的陷阱,或需要移植某个Python项目 使用__future__模块 print函数 整数除法 Unicode xrange 触发异常 处理异常 next()函数和.next()方法 For循环变量与全局命名空间泄漏 比较无序类型 使用input()解析输入内容 返回可迭代对象,而不是列表

推荐:《Python 2.7.x 和 3.x 版本的重要区别》

29 到底什么是Python?你可以在回答中与其他技术进行对比

答案 下面是一些关键点: • Python是一种解释型语言。这就是说,与C语言和C的衍生语言不同,Python代码在运行之前不需要编译。其他解释型语言还包括PHP和Ruby。 • Python是动态类型语言,指的是你在声明变量时,不需要说明变量的类型。你可以直接编写类似x=111和x="I'm a string"这样的代码,程序不会报错。 • Python非常适合面向对象的编程(OOP),因为它支持通过组合(composition)与继承(inheritance)的方式定义类(class)。Python中没有访问说明符(access specifier,类似C++中的public和private),这么设计的依据是“大家都是成年人了”。 • 在Python语言中,函数是第一类对象(first-class objects)。这指的是它们可以被指定给变量,函数既能返回函数类型,也可以接受函数作为输入。类(class)也是第一类对象。 • Python代码编写快,但是运行速度比编译语言通常要慢。好在Python允许加入基于C语言编写的扩展,因此我们能够优化代码,消除瓶颈,这点通常是可以实现的。numpy就是一个很好地例子,它的运行速度真的非常快,因为很多算术运算其实并不是通过Python实现的。 • Python用途非常广泛——网络应用,自动化,科学建模,大数据应用,等等。它也常被用作“胶水语言”,帮助其他语言和组件改善运行状况。 • Python让困难的事情变得容易,因此程序员可以专注于算法和数据结构的设计,而不用处理底层的细节。 为什么提这个问题: 如果你应聘的是一个Python开发岗位,你就应该知道这是门什么样的语言,以及它为什么这么酷。以及它哪里不好。

30 补充缺失的代码

def print_directory_contents(sPath): """ 这个函数接受文件夹的名称作为输入参数, 返回该文件夹中文件的路径, 以及其包含文件夹中文件的路径。 """ # 补充代码 答案 def print_directory_contents(sPath): import osfor sChild in os.listdir(sPath):sChildPath = os.path.join(sPath,sChild) if os.path.isdir(sChildPath): print_directory_contents(sChildPath) else: print sChildPath 特别要注意以下几点: • 命名规范要统一。如果样本代码中能够看出命名规范,遵循其已有的规范。 • 递归函数需要递归并终止。确保你明白其中的原理,否则你将面临无休无止的调用栈(callstack)。 • 我们使用os模块与操作系统进行交互,同时做到交互方式是可以跨平台的。你可以把代码写成sChildPath = sPath + '/' + sChild,但是这个在Windows系统上会出错。 • 熟悉基础模块是非常有价值的,但是别想破脑袋都背下来,记住Google是你工作中的良师益友。 • 如果你不明白代码的预期功能,就大胆提问。 • 坚持KISS原则!保持简单,不过脑子就能懂! 为什么提这个问题: • 说明面试者对与操作系统交互的基础知识 • 递归真是太好用啦

31 阅读下面的代码,写出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 A0] A3 = [A0[s] for s in A0] A4 = [i for i in A1 if i in A3] A5 = {i:ii for i in A1} A6 = [[i,ii] for i in A1] 答案 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]] 为什么提这个问题: • 列表解析(list comprehension)十分节约时间,对很多人来说也是一个大的学习障碍。 • 如果你读懂了这些代码,就很可能可以写下正确地值。 • 其中部分代码故意写的怪怪的。因为你共事的人之中也会有怪人。

32 下面代码会输出什么:

def f(x,l=[]): for i in range(x): l.append(i*i) print(l)

f(2) f(3,[3,2,1]) f(3) 答案: [0, 1] [3, 2, 1, 0, 1, 4] [0, 1, 0, 1, 4] 呃? 第一个函数调用十分明显,for循环先后将0和1添加至了空列表l中。l是变量的名字,指向内存中存储的一个列表。第二个函数调用在一块新的内存中创建了新的列表。l这时指向了新生成的列表。之后再往新列表中添加0、1和4。很棒吧。第三个函数调用的结果就有些奇怪了。它使用了之前内存地址中存储的旧列表。这就是为什么它的前两个元素是0和1了。

33 你如何管理不同版本的代码?

答案: 版本管理!被问到这个问题的时候,你应该要表现得很兴奋,甚至告诉他们你是如何使用Git(或是其他你最喜欢的工具)追踪自己和奶奶的书信往来。我偏向于使用Git作为版本控制系统(VCS),但还有其他的选择,比如subversion(SVN)。 为什么提这个问题: 因为没有版本控制的代码,就像没有杯子的咖啡。有时候我们需要写一些一次性的、可以随手扔掉的脚本,这种情况下不作版本控制没关系。但是如果你面对的是大量的代码,使用版本控制系统是有利的。版本控制能够帮你追踪谁对代码库做了什么操作;发现新引入了什么bug;管理你的软件的不同版本和发行版;在团队成员中分享源代码;部署及其他自动化处理。它能让你回滚到出现问题之前的版本,单凭这点就特别棒了。还有其他的好功能。怎么一个棒字了得!

34 “猴子补丁”(monkey patching)指的是什么?这种做法好吗?

答案: “猴子补丁”就是指,在函数或对象已经定义之后,再去改变它们的行为。 举个例子: import datetime datetime.datetime.now = lambda: datetime.datetime(2012, 12, 12) 大部分情况下,这是种很不好的做法 - 因为函数在代码库中的行为最好是都保持一致。打“猴子补丁”的原因可能是为了测试。mock包对实现这个目的很有帮助。 为什么提这个问题? 答对这个问题说明你对单元测试的方法有一定了解。你如果提到要避免“猴子补丁”,可以说明你不是那种喜欢花里胡哨代码的程序员(公司里就有这种人,跟他们共事真是糟糕透了),而是更注重可维护性。还记得KISS原则码?答对这个问题还说明你明白一些Python底层运作的方式,函数实际是如何存储、调用等等。 另外:如果你没读过mock模块的话,真的值得花时间读一读。这个模块非常有用。

35 阅读下面的代码,它的输出结果是什么?

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, 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): pass

a = A() b = B() c = C() d = D() e = E()

说明下列代码的输出结果

a.go() b.go() c.go() d.go() e.go()

a.stop() b.stop() 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!

stop C stop!

d.stop()

stop A stop!

stop C stop!

stop D stop!

e.stop()

stop A stop!

a.pause()

... Exception: Not Implemented

b.pause()

... Exception: Not Implemented

c.pause()

... Exception: Not Implemented

d.pause()

wait D wait!

e.pause()

...Exception: Not Implemented

为什么提这个问题? 因为面向对象的编程真的真的很重要。不骗你。答对这道问题说明你理解了继承和Python中super函数的用法。

36 阅读下面的代码,它的输出结果是什么?

<code>class Node(object):    def __init__(self,sName):        self._lChildren = []        self.sName = sName    def __repr__(self):        return "<node>".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):            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")oChild8 = 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)# 说明下面代码的输出结果oRoot.print_all_1()oRoot.print_all_2()/<node>/<code>
<code>答案oRoot.print_all_1()会打印下面的结果:<node><node><node><node><node><node><node><node><node><node><node>oRoot.print_all_1()会打印下面的结果:<node><node><node><node><node><node><node><node><node><node><node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<node>/<code>

为什么提这个问题? 因为对象的精髓就在于组合(composition)与对象构造(object construction)。对象需要有组合成分构成,而且得以某种方式初始化。这里也涉及到递归和生成器(generator)的使用。 生成器是很棒的数据类型。你可以只通过构造一个很长的列表,然后打印列表的内容,就可以取得与print_all_2类似的功能。生成器还有一个好处,就是不用占据很多内存。 有一点还值得指出,就是print_all_1会以深度优先(depth-first)的方式遍历树(tree),而print_all_2则是宽度优先(width-first)。有时候,一种遍历方式比另一种更合适。但这要看你的应用的具体情况。

37 介绍一下except的用法和作用?

答:try…except…except…[else…][finally…] 执行try下的语句,如果引发异常,则执行过程会跳到except语句。对每个except分支顺序尝试执行,如果引发的异常与except中的异常组匹配,执行相应的语句。如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。 try下的语句正常执行,则执行else块代码。如果发生异常,就不会执行 如果存在finally语句,最后总是会执行。

38 Python中pass语句的作用是什么?

答:pass语句不会执行任何操作,一般作为占位符或者创建占位程序,whileFalse:pass

39 介绍一下Python下range()函数的用法?

答:列出一组数据,经常用在for in range()循环中

40 Python里面match()和search()的区别?

答:re模块中match(pattern,string[,flags]),检查string的开头是否与pattern匹配。 re模块中research(pattern,string[,flags]),在string搜索pattern的第一个匹配值。

print(re.match(‘super’, ‘superstition’).span()) (0, 5) print(re.match(‘super’, ‘insuperable’)) None print(re.search(‘super’, ‘superstition’).span()) (0, 5) print(re.search(‘super’, ‘insuperable’).span()) (2, 7)

41 用Python匹配HTML tag的时候,<.>和<.>有什么区别?

答:术语叫贪婪匹配( <.> )和非贪婪匹配(<.> ) 例如: test <.> : test <.> :

42 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):返回指定范围内的浮点数。

43 有没有一个工具可以帮助查找python的bug和进行静态的代码分析?

答:PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告 Pylint是另外一个工具可以进行codingstandard检查

44 如何在一个function里面设置一个全局的变量?

答:解决方法是在function的开始插入一个global声明: def f() global x

45 单引号,双引号,三引号的区别

答:单引号和双引号是等效的,如果要换行,需要符号(),三引号则可以直接换行,并且可以包含注释 如果要表示Let’s go 这个字符串 单引号:s4 = ‘Let\\’s go’ 双引号:s5 = “Let’s go” s6 = ‘I realy like“python”!’ 这就是单引号和双引号都可以表示字符串的原因了

46 Python和多线程(multi-threading)。这是个好主意吗?列举一些让Python代码以并行方式运行的方法。

答案:Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的方法。 不过还是有很多理由促使我们使用threading包的。如果你想同时执行一些任务,而且不考虑效率问题,那么使用这个包是完全没问题的,而且也很方便。但是大部分情况下,并不是这么一回事,你会希望把多线程的部分外包给操作系统完成(通过开启多个进程),或者是某些调用你的Python代码的外部程序(例如Spark或Hadoop),又或者是你的Python代码调用的其他代码(例如,你可以在Python中调用C函数,用于处理开销较大的多线程工作)。 为什么提这个问题 因为GIL就是个混账东西(A-hole)。很多人花费大量的时间,试图寻找自己多线程代码中的瓶颈,直到他们明白GIL的存在。

47 将下面的函数按照执行效率高低排序。

它们都接受由0至1之间的数字构成的列表作为输入。这个列表可以很长。一个输入列表的示例如下:[random.random() for i in range(100000)]。你如何证明自己的答案是正确的。 def f1(lIn): l1 = sorted(lIn) l2 = [i for i in l1 if i<0.5] return [i*i for i in l2]

def f2(lIn): l1 = [i for i in lIn if i<0.5] l2 = sorted(l1) return [i*i for i in l2]

def f3(lIn): l1 = [ii for i in lIn] l2 = sorted(l1) return [i for i in l1 if i

答案:按执行效率从高到低排列:f2、f1和f3。要证明这个答案是对的,你应该知道如何分析自己代码的性能。Python中有一个很好的程序分析包,可以满足这个需求。 import cProfile lIn = [random.random() for i in range(100000)] cProfile.run('f1(lIn)') cProfile.run('f2(lIn)') cProfile.run('f3(lIn)')

为了向大家进行完整地说明,下面我们给出上述分析代码的输出结果:

<code>>>> cProfile.run('f1(lIn)')         4 function calls in 0.045 seconds   Ordered by: standard name   ncalls  tottime  percall  cumtime  percall filename:lineno(function)        1    0.009    0.009    0.044    0.044 <stdin>:1(f1)        1    0.001    0.001    0.045    0.045 <string>:1(<module>)        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}        1    0.035    0.035    0.035    0.035 {sorted}>>> cProfile.run('f2(lIn)')         4 function calls in 0.024 seconds   Ordered by: standard name   ncalls  tottime  percall  cumtime  percall filename:lineno(function)        1    0.008    0.008    0.023    0.023 <stdin>:1(f2)        1    0.001    0.001    0.024    0.024 <string>:1(<module>)        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}        1    0.016    0.016    0.016    0.016 {sorted}>>> cProfile.run('f3(lIn)')         4 function calls in 0.055 seconds   Ordered by: standard name   ncalls  tottime  percall  cumtime  percall filename:lineno(function)        1    0.016    0.016    0.054    0.054 <stdin>:1(f3)        1    0.001    0.001    0.055    0.055 <string>:1(<module>)        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}        1    0.038    0.038    0.038    0.038 {sorted}/<module>/<string>/<stdin>/<module>/<string>/<stdin>/<module>/<string>/<stdin>/<code>

为什么提这个问题? 定位并避免代码瓶颈是非常有价值的技能。想要编写许多高效的代码,最终都要回答常识上来——在上面的例子中,如果列表较小的话,很明显是先进行排序更快,因此如果你可以在排序前先进行筛选,那通常都是比较好的做法。其他不显而易见的问题仍然可以通过恰当的工具来定位。因此了解这些工具是有好处的。

48 如何用Python来进行查询和替换一个文本字符串?

可以使用sub()方法来进行查询和替换,sub方法的格式为:sub(replacement, string[, count=0]) replacement是被替换成的文本 string是需要被替换的文本 count是一个可选参数,指最大被替换的数量

49 Python里面search()和match()的区别?

match()函数只检测RE是不是在string的开始位置匹配,search()会扫描整个string查找匹配, 也就是说match()只有在0位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回none

50 用Python匹配HTML tag的时候,<.>和<.>有什么区别?

前者是贪婪匹配,会从头到尾匹配 xyz,而后者是非贪婪匹配,只匹配到第一个 >。

51 Python里面如何生成随机数?

import random random.random() 它会返回一个随机的0和1之间的浮点数

52 super init

super() lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.

Note that the syntax changed in Python 3.0: you can just say super().init() instead of super(ChildB, self).init() which IMO is quite a bit nicer. http://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods http://blog.csdn.net/mrlevo520/article/details/51712440

53 range and xrange

都在循环时使用,xrange内存性能更好。 for i in range(0, 20): for i in xrange(0, 20): What is the difference between range and xrange functions in Python 2.X? range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements. xrange is a sequence object that evaluates lazily. http://stackoverflow.com/questions/94935/what-is-the-difference-between-range-and-xrange-functions-in-python-2-x


分享到:


相關文章: