|
| 1 | +# Python面试题集 |
| 2 | + |
| 3 | +目前面了4家python的公司,基本都是很基础的东西,对比发现和stackoverflow上高票数的问题有很多重复,整理一下希望对别人有帮助. |
| 4 | + |
| 5 | +其中有可能是自己想到的一些知识点,也有一些是网上收集的. |
| 6 | + |
| 7 | +## 概念类 |
| 8 | + |
| 9 | +### 1 Python的函数参数传递 |
| 10 | + |
| 11 | +看两个例子: |
| 12 | + |
| 13 | +```python |
| 14 | +a = 1 |
| 15 | +def fun(a): |
| 16 | + a = 2 |
| 17 | +print a # 1 |
| 18 | +``` |
| 19 | + |
| 20 | +```python |
| 21 | +a = [] |
| 22 | +def fun(a): |
| 23 | + a.append(1) |
| 24 | +print a # [1] |
| 25 | +``` |
| 26 | + |
| 27 | +所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。 |
| 28 | + |
| 29 | +这里记住的是类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。(这就是这个问题的重点) |
| 30 | + |
| 31 | +当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改. |
| 32 | + |
| 33 | +如果还不明白的话,这里有更好的解释: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference |
| 34 | + |
| 35 | +### 2 Python中的元类(metaclass) |
| 36 | + |
| 37 | +这个非常的不常用,但是像ORM这种复杂的结构还是会需要的,详情请看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python |
| 38 | + |
| 39 | +### 3 @staticmethod和@classmethod |
| 40 | + |
| 41 | +Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下: |
| 42 | + |
| 43 | +```python |
| 44 | +def foo(x): |
| 45 | + print "executing foo(%s)"%(x) |
| 46 | + |
| 47 | +class A(object): |
| 48 | + def foo(self,x): |
| 49 | + print "executing foo(%s,%s)"%(self,x) |
| 50 | + |
| 51 | + @classmethod |
| 52 | + def class_foo(cls,x): |
| 53 | + print "executing class_foo(%s,%s)"%(cls,x) |
| 54 | + |
| 55 | + @staticmethod |
| 56 | + def static_foo(x): |
| 57 | + print "executing static_foo(%s)"%x |
| 58 | + |
| 59 | +a=A() |
| 60 | + |
| 61 | +``` |
| 62 | + |
| 63 | +这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定,对于一般的函数来说我们可以这么调用`foo(x)`,这个函数就是最常用的,它的工作跟任何东西(类,实例)无关.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是`foo(self, x)`,为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的`a.foo(x)`(其实是`foo(a, x)`).类方法一样,只不过它传递的是类而不是实例,`A.class_foo(x)`.注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好. |
| 64 | + |
| 65 | +对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用`a.static_foo(x)`或者`A.static_foo(x)`来调用. |
| 66 | + |
| 67 | +|\\|实例方法|类方法|静态方法| |
| 68 | +|:--|:--|:--|:--| |
| 69 | +|a = A()|a.foo(x)|a.class_foo(x)|a.static_foo(x)| |
| 70 | +|A|不可用|A.class_foo(x)|A.static_foo(x)| |
| 71 | + |
| 72 | +更多关于这个问题:http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python |
| 73 | + |
| 74 | +### 4 类变量和实例变量 |
| 75 | + |
| 76 | +```python |
| 77 | +class Person: |
| 78 | + name="aaa" |
| 79 | + |
| 80 | +p1=Person() |
| 81 | +p2=Person() |
| 82 | +p1.name="bbb" |
| 83 | +print p1.name # bbb |
| 84 | +print p2.name # aaa |
| 85 | +print Person.name # aaa |
| 86 | +``` |
| 87 | + |
| 88 | +类变量就是供类使用的变量,实例变量就是供实例使用的. |
| 89 | + |
| 90 | +这里`p1.name="bbb"`是实例调用了类变量,这其实和上面第一个问题一样,就是函数传参的问题,`p1.name`一开始是指向的类变量`name="aaa"`,但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了. |
| 91 | + |
| 92 | +可以看看下面的例子: |
| 93 | + |
| 94 | +```python |
| 95 | +class Person: |
| 96 | + name=[] |
| 97 | + |
| 98 | +p1=Person() |
| 99 | +p2=Person() |
| 100 | +p1.name.append(1) |
| 101 | +print p1.name # [1] |
| 102 | +print p2.name # [1] |
| 103 | +print Person.name # [1] |
| 104 | +``` |
| 105 | + |
| 106 | +参考:http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block |
| 107 | + |
| 108 | +### 5 Python自省 |
| 109 | + |
| 110 | +这个也是python彪悍的特性. |
| 111 | + |
| 112 | +自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance(). |
| 113 | + |
| 114 | +### 6 字典推导式 |
| 115 | + |
| 116 | +可能你见过列表推导时,却没有见过字典推导式,在2.7中才加入的: |
| 117 | + |
| 118 | +```python |
| 119 | +d = {key: value for (key, value) in iterable} |
| 120 | +``` |
| 121 | + |
| 122 | +### 7 Python中单下划线和双下划线 |
| 123 | + |
| 124 | +```python |
| 125 | +>>> class MyClass(): |
| 126 | +... def __init__(self): |
| 127 | +... self.__superprivate = "Hello" |
| 128 | +... self._semiprivate = ", world!" |
| 129 | +... |
| 130 | +>>> mc = MyClass() |
| 131 | +>>> print mc.__superprivate |
| 132 | +Traceback (most recent call last): |
| 133 | + File "<stdin>", line 1, in <module> |
| 134 | +AttributeError: myClass instance has no attribute '__superprivate' |
| 135 | +>>> print mc._semiprivate |
| 136 | +, world! |
| 137 | +>>> print mc.__dict__ |
| 138 | +{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'} |
| 139 | +``` |
| 140 | + |
| 141 | +`__foo__`:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突. |
| 142 | + |
| 143 | +`_foo`:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式. |
| 144 | + |
| 145 | +`__foo`:这个有真正的意义:解析器用`_classname__foo`来代替这个名字,以区别和其他类相同的命名. |
| 146 | + |
| 147 | +详情见:http://stackoverflow.com/questions/1301346/the-meaning-of-a-single-and-a-double-underscore-before-an-object-name-in-python |
| 148 | + |
| 149 | +或者: http://www.zhihu.com/question/19754941 |
| 150 | + |
| 151 | +### 8 字符串格式化:%和.format |
| 152 | + |
| 153 | +.format在许多方面看起来更便利.你可以重用参数,但是你用%就不行.最烦人的是%它无法同时传递一个变量和元组.你可能会想下面的代码不会有什么问题: |
| 154 | + |
| 155 | +``` |
| 156 | +"hi there %s" % name |
| 157 | +``` |
| 158 | + |
| 159 | +但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做: |
| 160 | + |
| 161 | +``` |
| 162 | +"hi there %s" % (name,) # 提供一个单元素的数组而不是一个参数 |
| 163 | +``` |
| 164 | + |
| 165 | +但是有点丑..format就没有这些问题.你给的第二个问题也是这样,.format好看多了. |
| 166 | + |
| 167 | +你为什么不用它? |
| 168 | + |
| 169 | +* 不知道它(在读这个之前) |
| 170 | +* 为了和Python2.5兼容 |
| 171 | + |
| 172 | +http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format |
| 173 | + |
| 174 | +### 9 迭代器和生成器 |
| 175 | + |
| 176 | +这个是stackoverflow里python排名第一的问题,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python |
| 177 | + |
| 178 | +这是中文版: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/1/README.html |
| 179 | + |
| 180 | +### 10 `*args` and `**kwargs` |
| 181 | + |
| 182 | +http://stackoverflow.com/questions/3394835/args-and-kwargs |
0 commit comments