Python

Python语言特性

1 Python的函数参数传递

看两个例子:

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

所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。

这里记住的是类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。(这就是这个问题的重点)

当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.

如果还不明白的话,这里有更好的解释: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference

2 Python中的元类(metaclass)

这个非常的不常用,但是像ORM这种复杂的结构还是会需要的,详情请看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

3 @staticmethod和@classmethod

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

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)"%x

a=A()

这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定,对于一般的函数来说我们可以这么调用foo(x),这个函数就是最常用的,它的工作跟任何东西(类,实例)无关.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是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)

更多关于这个问题:http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python

4 类变量和实例变量

class Person:
    name="aaa"

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了.

可以看看下面的例子:

class Person:
    name=[]

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

参考:http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block

5 Python自省

这个也是python彪悍的特性.

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

6 字典推导式

可能你见过列表推导时,却没有见过字典推导式,在2.7中才加入的:

d = {key: value for (key, value) in iterable}

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

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}

__foo__:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突.

_foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.

__foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名.

详情见:http://stackoverflow.com/questions/1301346/the-meaning-of-a-single-and-a-double-underscore-before-an-object-name-in-python

或者: http://www.zhihu.com/question/19754941

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

.format在许多方面看起来更便利.对于%最烦人的是它无法同时传递一个变量和元组.你可能会想下面的代码不会有什么问题:

"hi there %s" % name

但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做:

"hi there %s" % (name,)   # 提供一个单元素的数组而不是一个参数

但是有点丑..format就没有这些问题.你给的第二个问题也是这样,.format好看多了.

你为什么不用它?

  • 不知道它(在读这个之前)
  • 为了和Python2.5兼容(譬如logging库建议使用%(issue #4))

http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format

9 迭代器和生成器

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

这是中文版: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/1/README.html

10 *args and **kwargs

*args**kwargs只是为了方便并没有强制使用它们.

当你不确定你的函数里将要传递多少参数时你可以用*args.例如,它可以传递任意数量的参数:

>>> def print_everything(*args):
        for count, thing in enumerate(args):
...         print '{0}. {1}'.format(count, thing)
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage

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

>>> def table_things(**kwargs):
...     for name, value in kwargs.items():
...         print '{0} = {1}'.format(name, value)
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit

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

def table_things(titlestring, **kwargs)

*args**kwargs可以同时在函数的定义中,但是*args必须在**kwargs前面.

当调用函数时你也可以用***语法.例如:

>>> def print_three_things(a, b, c):
...     print 'a = {0}, b = {1}, c = {2}'.format(a,b,c)
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)

a = aardvark, b = baboon, c = cat

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

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 新式类和旧式类

这个面试官问了,我说了老半天,不知道他问的真正意图是什么.

stackoverflow

这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html

新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类是广度优先,旧式类是深度优先),里讲的也很多.

15 __new____init__的区别

这个__new__确实很少见到,先做了解吧.

  1. __new__是一个静态方法,而__init__是一个实例方法.
  2. __new__方法会返回一个创建的实例,而__init__什么都不返回.
  3. 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
  4. 当创建一个新实例时调用__new__,初始化一个实例时用__init__.

stackoverflow

ps: __metaclass__是创建类时起作用.所以我们可以分别使用__metaclass__,__new____init__来分别在类创建,实例创建和实例初始化的时候做一些小手脚.

16 单例模式

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

1 使用__new__方法

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._instance

class MyClass(Singleton):
    a = 1

2 共享属性

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


class Borg(object):
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super(Borg, cls).__new__(cls, *args, **kw)
        ob.__dict__ = cls._state
        return ob

class MyClass2(Borg):
    a = 1

3 装饰器版本

def singleton(cls, *args, **kw):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

4 import方法

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

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

# to use
from mysingleton import my_singleton

my_singleton.foo()

17 Python中的作用域

Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。

当 Python 遇到一个变量的话他会按照这样的顺序进行搜索:

本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)

18 GIL线程全局锁

线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.

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的元素的序列。

>>>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

23 Python里的拷贝

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

import copy
a = [1, 2, 3, 4, ['a', 'b']]  #原始对象

b = a  #赋值,传对象的引用
c = copy.copy(a)  #对象拷贝,浅拷贝
d = copy.deepcopy(a)  #对象拷贝,深拷贝

a.append(5)  #修改对象a
a[4].append('c')  #修改对象a中的['a', 'b']数组对象

print 'a = ', a
print 'b = ', b
print 'c = ', c
print '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']]

24 Python垃圾回收机制

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

1 引用计数

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

优点:

  1. 简单
  2. 实时性

缺点:

  1. 维护引用计数消耗资源
  2. 循环引用

2 标记-清除机制

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

3 分代技术

分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。

Python默认定义了三代对象集合,索引数越大,对象存活时间越长。

举例:
当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。

25 Python的List

推荐: http://www.jianshu.com/p/J4U6rR

26 Python的is

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

27 read,readline和readlines

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

28 Python2和3的区别

操作系统

1 select,poll和epoll

其实所有的I/O都是轮询的方法,只不过实现的层面不同罢了.

这个问题可能有点深入了,但相信能回答出这个问题是对I/O多路复用有很好的了解了.其中tornado使用的就是epoll的.

selec,poll和epoll区别总结

基本上select有3个缺点:

  1. 连接数受限
  2. 查找配对速度慢
  3. 数据由内核拷贝到用户态

poll改善了第一个缺点

epoll改了三个缺点.

关于epoll的: http://www.cnblogs.com/my_life/articles/3968782.html

2 调度算法

  1. 先来先服务
  2. 短作业优先
  3. 最高优先权调度
  4. 时间片轮转
  5. 多级反馈队列调度

实时调度算法:

  1. 最早截至时间优先 EDF
  2. 最低松弛度优先 LLF

3 死锁

原因:

  1. 竞争资源
  2. 程序推进顺序不当

必要条件:

  1. 互斥条件
  2. 请求和保持条件
  3. 不剥夺条件
  4. 环路等待条件

处理死锁基本方法:

  1. 预防死锁(摒弃除1以外的条件)
  2. 避免死锁(银行家算法)
  3. 检测死锁(资源分配图)
  4. 解除死锁
    1. 剥夺资源
    2. 撤销进程

4 程序编译与链接

推荐: http://www.ruanyifeng.com/blog/2014/11/compiler.html

Bulid过程可以分解为4个步骤:预处理(Prepressing), 编译(Compilation)、汇编(Assembly)、链接(Linking)

以c语言为例:

1 预处理

预编译过程主要处理那些源文件中的以“#”开始的预编译指令,主要处理规则有:

  1. 将所有的“#define”删除,并展开所用的宏定义
  2. 处理所有条件预编译指令,比如“#if”、“#ifdef”、 “#elif”、“#endif”
  3. 处理“#include”预编译指令,将被包含的文件插入到该编译指令的位置,注:此过程是递归进行的
  4. 删除所有注释
  5. 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时可显示行号
  6. 保留所有的#pragma编译器指令。

2 编译

编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分。

3 汇编

汇编器是将汇编代码转化成机器可以执行的指令,每一条汇编语句几乎都是一条机器指令。经过编译、链接、汇编输出的文件成为目标文件(Object File)

4 链接

链接的主要内容就是把各个模块直接爱你相互引用的部分处理好,使各个模块可以正确的拼接。
链接的主要过程包块 地址和空间的分配(Address and Storage Allocation)、符号决议(Symbol Resolution)和重定位(Relocation)等步骤。

5 静态链接和动态链接

静态链接方法:静态链接的时候,载入代码就会把程序会用到的动态代码或动态代码的地址确定下来
静态库的链接可以使用静态链接,动态链接库也可以使用这种方法链接导入库

动态链接方法:使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这种方式使程序初始化时间较短,但运行期间的性能比不上静态链接的程序

6 虚拟内存技术

虚拟存储器是值具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储系统.

7 分页和分段

分页: 用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。

分段: 将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。

分页与分段的主要区别

  1. 页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要.段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要.
  2. 页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的.而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分.
  3. 分页的作业地址空间是一维的.分段的地址空间是二维的.

8 页面置换算法

  1. 最佳置换算法OPT:不可能实现
  2. 先进先出FIFO
  3. 最近最久未使用算法LRU:最近一段时间里最久没有使用过的页面予以置换.
  4. clock算法

9 边沿触发和水平触发

边缘触发是指每当状态变化时发生一个 io 事件,条件触发是只要满足条件就发生一个 io 事件

数据库

1 事务

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。

2 数据库索引

推荐: http://tech.meituan.com/mysql-index.html

MySQL索引背后的数据结构及算法原理

聚集索引,非聚集索引,B-Tree,B+Tree,最左前缀原理

3 Redis原理

4 乐观锁和悲观锁

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

5 MVCC

6 MyISAM和InnoDB

MyISAM 适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。

InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。他是它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。

网络

1 三次握手

  1. 客户端通过向服务器端发送一个SYN来创建一个主动打开,作为三路握手的一部分。客户端把这段连接的序号设定为随机数 A。
  2. 服务器端应当为一个合法的SYN回送一个SYN/ACK。ACK 的确认码应为 A+1,SYN/ACK 包本身又有一个随机序号 B。
  3. 最后,客户端再发送一个ACK。当服务端受到这个ACK的时候,就完成了三路握手,并进入了连接创建状态。此时包序号被设定为收到的确认号 A+1,而响应则为 B+1。

2 四次挥手

3 ARP协议

地址解析协议(Address Resolution Protocol): 根据IP地址获取物理地址的一个TCP/IP协

4 urllib和urllib2的区别

这个面试官确实问过,当时答的urllib2可以Post而urllib不可以.

  1. urllib提供urlencode方法用来GET查询字符串的产生,而urllib2没有。这是为何urllib常和urllib2一起使用的原因。
  2. urllib2可以接受一个Request类的实例来设置URL请求的headers,urllib仅可以接受URL。这意味着,你不可以伪装你的User Agent字符串等。

5 Post和Get

6 Cookie和Session

7 apache和nginx的区别

8 网站用户密码保存

  1. 明文保存
  2. 明文hash后保存,如md5
  3. MD5+Salt方式,这个salt可以随机
  4. 知乎使用了Bcrypy(好像)加密

9 HTTP和HTTPS

状态码 定义
1xx 报告 接收到请求,继续进程
2xx 成功 步骤成功接收,被理解,并被接受
3xx 重定向 为了完成请求,必须采取进一步措施
4xx 客户端出错 请求包括错的顺序或不能完成
5xx 服务器出错 服务器无法完成显然有效的请求

403: Forbidden
404: Not Found

HTTPS握手,对称加密,非对称加密,TLS/SSL,RSA

10 XSRF和XSS

  • CSRF(Cross-site request forgery)跨站请求伪造
  • XSS(Cross Site Scripting)跨站脚本攻击

CSRF重点在请求,XSS重点在脚本

11 幂等 Idempotence

HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。(注意是副作用)

GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足幂等性的。

DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此,调用者可以多次调用或刷新页面而不必担心引起错误。

POST所对应的URI并非创建的资源本身,而是资源的接收者。比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI;所以,POST方法不具备幂等性。

PUT所对应的URI是要创建或更新的资源本身。比如:PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有幂等性。

12 RESTful架构(SOAP,RPC)

推荐: http://www.ruanyifeng.com/blog/2011/09/restful.html

13 SOAP

SOAP(原为Simple Object Access Protocol的首字母缩写,即简单对象访问协议)是交换数据的一种协议规范,使用在计算机网络Web服务(web service)中,交换带结构信息。SOAP为了简化网页服务器(Web Server)从XML数据库中提取数据时,节省去格式化页面时间,以及不同应用程序之间按照HTTP通信协议,遵从XML格式执行资料互换,使其抽象于语言实现、平台和硬件。

14 RPC

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

总结:服务提供的两大流派.传统意义以方法调用为导向通称RPC。为了企业SOA,若干厂商联合推出webservice,制定了wsdl接口定义,传输soap.当互联网时代,臃肿SOA被简化为http+xml/json.但是简化出现各种混乱。以资源为导向,任何操作无非是对资源的增删改查,于是统一的REST出现了.

进化的顺序: RPC -> SOAP -> RESTful

15 CGI和WSGI

16 中间人攻击

在GFW里屡见不鲜的,呵呵.

中间人攻击(Man-in-the-middle attack,通常缩写为MITM)是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。

17 c10k问题

所谓c10k问题,指的是服务器同时支持成千上万个客户端的问题,也就是concurrent 10 000 connection(这也是c10k这个名字的由来)。

18 socket

推荐: http://www.360doc.com/content/11/0609/15/5482098_122692444.shtml

Socket=Ip address+ TCP/UDP + port

19 浏览器缓存

推荐: http://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html

304 Not Modified

20 HTTP1.0和HTTP1.1

推荐: http://blog.csdn.net/elifefly/article/details/3964766

  1. 请求头Host字段,一个服务器多个网站
  2. 长链接
  3. 文件断点续传
  4. 身份认证,状态管理,Cache缓存

21 Ajax

*NIX

unix进程间通信方式(IPC)

  1. 管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
  2. 命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
  3. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
  4. 消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
  5. 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
  6. 内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
  7. 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
  8. 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

数据结构

1 红黑树

红黑树与AVL的比较:

AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多;

红黑是用非严格的平衡来换取增删节点时候旋转次数的降低;

所以简单说,如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL,如果搜索,插入删除次数几乎差不多,应该选择RB。

编程题

1 台阶问题/斐波纳挈

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

fib = lambda n: 1 if n <= 2 else fib(n - 1) + fib(n - 2)

第二种记忆方法

def memo(func): 
    cache={}    
    def wrap(*args): 
        if args not in cache: 
            cache[args]=func(*args) 
        return cache[args] 
    return wrap 

@memo 
def fib(i): 
    if i<2: 
        return 1 
    return fib(i-1)+fib(i-2)

第三种方法

def fib(n):
    a, b = 0, 1
    while a < n:
        print a,
        a, b  = b, a + b
    print
fib(1000)

2 变态台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

fib = lambda n: i if n < 2 else 2 * fib(n - 1)

3 矩形覆盖

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

2*n个矩形的覆盖方法等于第2*(n-1)加上第2*(n-2)的方法。

f = lambda n: 1 if n < 2 else f(n - 1) + f(n - 2)

4 杨氏矩阵查找

在一个m行n列二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

5 去除列表中的重复元素

用集合

list(set(l))

用字典

l1 = ['b','c','d','b','c','a','a']
l2 = {}.fromkeys(l1).keys()
print l2

用字典并保持顺序

l1 = ['b','c','d','b','c','a','a']
l2 = list(set(l1))
l2.sort(key=l1.index)
print l2

列表推导式

l1 = ['b','c','d','b','c','a','a']
l2 = []
[l2.append(i) for i in l1 if not i in l2]

面试官提到的,先排序然后删除.

6 链表成对调换

1->2->3->4转换成2->1->4->3.

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    # @param a ListNode
    # @return a ListNode
    def swapPairs(self, head):
        if head != None and head.next != None:
            next = head.next
            head.next = self.swapPairs(next.next)
            next.next = head
            return next
        return head

7 创建字典的方法

1 直接创建

dict = {'name':'earth', 'port':'80'}

2 工厂方法

items=[('name','earth'),('port','80')]
dict2=dict(items)
dict1=dict((['name','earth'],['port','80']))

3 fromkeys()方法

dict1={}.fromkeys(('x','y'),-1)
dict={'x':-1,'y':-1}
dict2={}.fromkeys(('x','y'))
dict2={'x':None, 'y':None}

8 合并两个有序列表

知乎远程面试要求编程

尾递归

def _recursion_merge_sort2(l1, l2, tmp):
    if len(l1) == 0 or len(l2) == 0:
        tmp.extend(l1)
        tmp.extend(l2)
        return tmp
    else:
        if l1[0] < l2[0]:
            tmp.append(l1[0])
            del l1[0]
        else:
            tmp.append(l2[0])
            del l2[0]
        return _recursion_merge_sort2(l1, l2, tmp)

def recursion_merge_sort2(l1, l2):
    return _recursion_merge_sort2(l1, l2, [])

循环算法

def loop_merge_sort(l1, l2):
    tmp = []
    while len(l1) > 0 and len(l2) > 0:
        if l1[0] < l2[0]:
            tmp.append(l1[0])
            del l1[0]
        else:
            tmp.append(l2[0])
            del l2[0]
    tmp.extend(l1)
    tmp.extend(l2)
    return tmp

9 交叉链表求交点

去哪儿的面试,没做出来.

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
def node(l1, l2):
    length1, lenth2 = 0, 0
    # 求两个链表长度
    while l1.next:
        l1 = l1.next
        length1 += 1
    while l2.next:
        l2 = l2.next
        length2 += 1
    # 长的链表先走
    if length1 > lenth2:
        for _ in range(length1 - length2):
            l1 = l1.next
    else:
        for _ in range(length2 - length1):
            l2 = l2.next
    while l1 and l2:
        if l1.next == l2.next:
            return l1.next
        else:
            l1 = l1.next
            l2 = l2.next

10 二分查找

def binarySearch(l, t):
    low, high = 0, len(l) - 1
    while low < high:
        print low, high
        mid = (low + high) / 2
        if l[mid] > t:
            high = mid
        elif l[mid] < t:
            low = mid + 1
        else:
            return mid
    return False

if __name__ == '__main__':
    l = [1, 4, 12, 45, 66, 99, 120, 444]
    print binarySearch(l, 12)
    print binarySearch(l, 1)
    print binarySearch(l, 13)

11 快排

def qsort(seq):
    if seq==[]:
        return []
    else:
        pivot=seq[0]
        lesser=qsort([x for x in seq[1:] if x<pivot])
        greater=qsort([x for x in seq[1:] if x>=pivot])
        return lesser+[pivot]+greater

if __name__=='__main__':
    seq=[5,6,78,9,0,-1,2,3,-65,12]
    print(qsort(seq))

12 找零问题

def  coinChange(values, money, coinsUsed):
    #values    T[1:n]数组
    #valuesCounts   钱币对应的种类数
    #money  找出来的总钱数
    #coinsUsed   对应于目前钱币总数i所使用的硬币数目
    for cents in range(1, money+1):
        minCoins = cents     #从第一个开始到money的所有情况初始
        for value in values:
            if value <= cents:
                temp = coinsUsed[cents - value] + 1
                if temp < minCoins:
                    minCoins = temp
        coinsUsed[cents] = minCoins
        print('面值为:{0} 的最小硬币数目为:{1} '.format(cents, coinsUsed[cents]) )

if __name__ == '__main__':
    values = [ 25, 21, 10, 5, 1]
    money = 63
    coinsUsed = {i:0 for i in range(money+1)}
    coinChange(values, money, coinsUsed)

13 广度遍历和深度遍历二叉树

给定一个数组,构建二叉树,并且按层次打印这个二叉树

## 14 二叉树节点
class Node(object):
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

tree = Node(1, Node(3, Node(7, Node(0)), Node(6)), Node(2, Node(5), Node(4)))

## 15 层次遍历
def lookup(root):
    stack = [root]
    while stack:
        current = stack.pop(0)
        print current.data
        if current.left:
            stack.append(current.left)
        if current.right:
            stack.append(current.right)
## 16 深度遍历
def deep(root):
    if not root:
        return
    print root.data
    deep(root.left)
    deep(root.right)

if __name__ == '__main__':
    lookup(tree)
    deep(tree)

17 前中后序遍历

深度遍历改变顺序就OK了

18 求最大树深

def maxDepth(root):
        if not root:
            return 0
        return max(maxDepth(root.left), maxDepth(root.right)) + 1

19 求两棵树是否相同

def isSameTree(p, q):
    if p == None and q == None:
        return True
    elif p and q :
        return p.val == q.val and isSameTree(p.left,q.left) and isSameTree(p.right,q.right)
    else :
        return False

20 前序中序求后序

推荐: http://blog.csdn.net/hinyunsin/article/details/6315502

def rebuild(pre, center):
    if not pre:
        return
    cur = Node(pre[0])
    index = center.index(pre[0])
    cur.left = rebuild(pre[1:index + 1], center[:index])
    cur.right = rebuild(pre[index + 1:], center[index + 1:])
    return cur

def deep(root):
    if not root:
        return
    deep(root.left)
    deep(root.right)
    print root.data

21 单链表逆置

class Node(object):
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next

link = Node(1, Node(2, Node(3, Node(4, Node(5, Node(6, Node(7, Node(8, Node(9)))))))))

def rev(link):
    pre = link
    cur = link.next
    pre.next = None
    while cur:
        tmp = cur.next
        cur.next = pre
        pre = cur
        cur = tmp
    return pre

root = rev(link)
while root:
    print root.data
    root = root.next

ExtendList

What will be the output of the code below? Explain your answer.

def extendList(val, list=[]):
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

How would you modify the definition of extendList to produce the presumably desired behavior?

Solution

The output of the above code will be:

list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

Many will mistakenly expect list1 to be equal to [10] and list3 to be equal to ['a'], thinking that the list argument will be set to its default value of [] each time extendList is called.

However, what actually happens is that the new default list is created only once when the function is defined, and that same list is then used subsequently whenever extendList is invoked without a list argument being specified. This is because expressions in default arguments are calculated when the function is defined, not when it’s called.

list1 and list3 are therefore operating on the same default list, whereas list2 is operating on a separate list that it created (by passing its own empty list as the value for the list parameter).

The definition of the extendList function could be modified as follows, though, to always begin a new list when no list argument is specified, which is more likely to have been the desired behavior:

def extendList(val, list=None):
    if list is None:
        list = []
    list.append(val)
    return list

With this revised implementation, the output would be:

list1 = [10]
list2 = [123]
list3 = ['a']

late binding

What will be the output of the code below? Explain your answer.

def multipliers():
    return [lambda x : i * x for i in range(4)]
    
print [m(2) for m in multipliers()]

How would you modify the definition of multipliers to produce the presumably desired behavior?

Solution

The output of the above code will be 6, 6, 6, 6.

The reason for this is that Python’s closures are late binding. This means that the values of variables used in closures are looked up at the time the inner function is called. So as a result, when any of the functions returned by multipliers() are called, the value of i is looked up in the surrounding scope at that time. By then, regardless of which of the returned functions is called, the for loop has completed and i is left with its final value of 3. Therefore, every returned function multiplies the value it is passed by 3, so since a value of 2 is passed in the above code, they all return a value of 6 (i.e., 3 x 2).

(Incidentally, as pointed out in The Hitchhiker’s Guide to Python, there is a somewhat widespread misconception that this has something to do with lambdas, which is not the case. Functions created with a lambda expression are in no way special and the same behavior is exhibited by functions created using an ordinary def.)

Below are a few examples of ways to circumvent this issue.

One solution would be use a Python generator as follows:

def multipliers():
     for i in range(4): yield lambda x : i * x 

Another solution is to create a closure that binds immediately to its arguments by using a default argument. For example:

def multipliers():
    return [lambda x, i=i : i * x for i in range(4)]

Or alternatively, you can use the functools.partial function:

from functools import partial
from operator import mul

def multipliers():
    return [partial(mul, i) for i in range(4)]

class variables

What will be the output of the code below? Explain your answer.

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass

print Parent.x, Child1.x, Child2.x
Child1.x = 2
print Parent.x, Child1.x, Child2.x
Parent.x = 3
print Parent.x, Child1.x, Child2.x

Solution

The output of the above code will be:

1 1 1
1 2 1
3 2 3

What confuses or surprises many about this is that the last line of output is 3 2 3 rather than 3 2 1. Why does changing the value of Parent.x also change the value of Child2.x, but at the same time not change the value of Child1.x?

The key to the answer is that, in Python, class variables are internally handled as dictionaries. If a variable name is not found in the dictionary of the current class, the class hierarchy (i.e., its parent classes) are searched until the referenced variable name is found (if the referenced variable name is not found in the class itself or anywhere in its hierarchy, an AttributeError occurs).

Therefore, setting x = 1 in the Parent class makes the class variable x (with a value of 1) referenceable in that class and any of its children. That’s why the first print statement outputs 1 1 1.

Subsequently, if any of its child classes overrides that value (for example, when we execute the statement Child1.x = 2), then the value is changed in that child only. That’s why the second print statement outputs 1 2 1.

Finally, if the value is then changed in the Parent (for example, when we execute the statement Parent.x = 3), that change is reflected also by any children that have not yet overridden the value (which in this case would be Child2). That’s why the third print statement outputs 3 2 3.

python 2 / 3

What will be the output of the code below in Python 2? Explain your answer.

def div1(x,y):
    print "%s/%s = %s" % (x, y, x/y)
    
def div2(x,y):
    print "%s//%s = %s" % (x, y, x//y)

div1(5,2)
div1(5.,2)
div2(5,2)
div2(5.,2.)

Also, how would the answer differ in Python 3 (assuming, of course, that the above print statements were converted to Python 3 syntax)?

Solution

In Python 2, the output of the above code will be:

5/2 = 2
5.0/2 = 2.5
5//2 = 2
5.0//2.0 = 2.0

By default, Python 2 automatically performs integer arithmetic if both operands are integers. As a result, 5/2 yields 2, while 5./2 yields 2.5.

Note that you can override this behavior in Python 2 by adding the following import:

from __future__ import division 

Also note that the “double-slash” (//) operator will always perform integer division, regardless of the operand types. That’s why 5.0//2.0 yields 2.0 even in Python 2.

Python 3, however, does not have this behavior; i.e., it does not perform integer arithmetic if both operands are integers. Therefore, in Python 3, the output will be as follows:

5/2 = 2.5
5.0/2 = 2.5
5//2 = 2
5.0//2.0 = 2.0

IndexError

What will be the output of the code below?

list = ['a', 'b', 'c', 'd', 'e']
print list[10:]

Solution

The above code will output [], and will not result in an IndexError.

As one would expect, attempting to access a member of a list using an index that exceeds the number of members (e.g., attempting to access list[10] in the list above) results in an IndexError. However, attempting to access a slice of a list at a starting index that exceeds the number of members in the list will not result in an IndexError and will simply return an empty list.

What makes this a particularly nasty gotcha is that it can lead to bugs that are really hard to track down since no error is raised at runtime.

List Tricks

Consider the following code snippet:

1. list = [ [ ] ] * 5
2. list  # output?
3. list[0].append(10)
4. list  # output?
5. list[1].append(20)
6. list  # output?
7. list.append(30)
8. list  # output?

What will be the ouput of lines 2, 4, 6, and 8? Explain your answer.

Solution

The output will be as follows:

[[], [], [], [], []]
[[10], [10], [10], [10], [10]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]

Here’s why:

The first line of output is presumably intuitive and easy to understand; i.e., list = [ [ ] ] * 5 simply creates a list of 5 lists.

However, the key thing to understand here is that the statement list = [ [ ] ] * 5 does NOT create a list containing 5 distinct lists; rather, it creates a a list of 5 references to the same list. With this understanding, we can better understand the rest of the output.

list[0].append(10) appends 10 to the first list. But since all 5 lists refer to the same list, the output is: [[10], [10], [10], [10], [10]].

Similarly, list[1].append(20) appends 20 to the second list. But again, since all 5 lists refer to the same list, the output is now: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]].

In contrast, list.append(30) is appending an entirely new element to the “outer” list, which therefore yields the output: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30].

Selection

Given a list of N numbers, use a single list comprehension to produce a new list that only contains those values that are:
(a) even numbers, and
(b) from elements in the original list that had even indices

For example, if list[2] contains a value that is even, that value should be included in the new list, since it is also at an even index (i.e., 2) in the original list. However, if list[3] contains an even number, that number should not be included in the new list since it is at an odd index (i.e., 3) in the original list.

Solution

A simple solution to this problem would be as follows

[x for x in list[::2] if x%2 == 0]

For example, given the following list:

#        0   1   2   3    4    5    6    7    8
list = [ 1 , 3 , 5 , 8 , 10 , 13 , 18 , 36 , 78 ]

the list comprehension [x for x in list[::2] if x%2 == 0] will evaluate to:

[10, 18, 78]

The expression works by first taking the numbers that are at the even indices, and then filtering out all the odd numbers.

Subclass of dictionary

Given the following subclass of dictionary:

class DefaultDict(dict):
    def __missing__(self, key):
        return []

Will the code below work? Why or why not?

d = DefaultDict()
d['florp'] = 127

Solution

Yes, it will work. With this implementation of the DefaultDict class, whenever a key is missing, the instance of the dictionary will automatically be instantiated with a list.


== 和 is 的区别

Python 中的对象包括3要素,id(唯一标示), type(类型), value(值)

is 用来判断,a 对象是否是 b 对象,用id来判断

== 用来判断 a 对象的值是否与 b的值相同

说说你对zen of python的理解,你有什么办法看到它?

Python之禅,Python秉承一种独特的简洁和可读行高的语法,以及高度一致的编程模式,符合“大脑思维习惯”,使Python易于学习、理解和记忆。Python同时采用了一条极简主义的设计理念,了解完整的Python哲学理念,可以在任何一个Python交互解释器中键入import this命令,这是Python隐藏的一个彩蛋:描绘了一系列Python设计原则。如今已是Python社区内流行的行话EIBTI,明了胜于晦涩这条规则的简称. 在Python的思维方式中,明了胜于晦涩,简洁胜于复杂。

>>> import this    
The Zen of Python, by Tim Peters    
    
Beautiful is better than ugly.    
Explicit is better than implicit.    
Simple is better than complex.    
Complex is better than complicated.    
Flat is better than nested.    
Sparse is better than dense.    
Readability counts.    
Special cases aren't special enough to break the rules.    
Although practicality beats purity.    
Errors should never pass silently.    
Unless explicitly silenced.    
In the face of ambiguity, refuse the temptation to guess.    
There should be one-- and preferably only one --obvious way to do it.    
Although that way may not be obvious at first unless you're Dutch.    
Now is better than never.    
Although never is often better than *right* now.    
If the implementation is hard to explain, it's a bad idea.    
If the implementation is easy to explain, it may be a good idea.    
Namespaces are one honking great idea -- let's do more of those!  

说说你对pythonic的看法,尝试解决下面的小问题

简洁,明了,严谨,灵活

#交换两个变量值    
a,b = b,a    
    
#去掉list中的重复元素    
old_list = [1,1,1,3,4]    
new_list = list(set(old_list))    
    
#翻转一个字符串    
s = 'abcde'    
ss = s[::-1]    
    
#用两个元素之间有对应关系的list构造一个dict    
names = ['jianpx', 'yue']    
ages = [23, 40]    
m = dict(zip(names,ages))    
    
#将数量较多的字符串相连,如何效率较高,为什么    
fruits = ['apple', 'banana']    
result = ''.join(fruits)    
    
#python字符串效率问题之一就是在连接字符串的时候使用‘+’号,例如 s = ‘s1’ + ‘s2’ + ‘s3’ + ...+’sN’,总共将N个字符串连接起来, 但是使用+号的话,python需要申请N-1次内存空间, 然后进行字符串拷贝。原因是字符串对象PyStringObject在python当中是不可变 对象,所以每当需要合并两个字符串的时候,就要重新申请一个新的内存空间 (大小为两个字符串长度之和)来给这个合并之后的新字符串,然后进行拷贝。 所以用+号效率非常低。建议在连接字符串的时候使用字符串本身的方法 join(list),这个方法能提高效率,原因是它只是申请了一次内存空间, 因为它可以遍历list中的元素计算出总共需要申请的内存空间的大小,一次申请完。  

你调试python代码的方法有哪些?

  • 具体IDE都有调试,比如:IDLE, Eclipse+Pydev都可以设置断点调试。
  • pdb模块也可以做调试。
  • 还有PyChecker和Pylint
  • PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告

什么是GIL?

什么是GIL(Global Interpreter Lock)全局解释器锁? 简单地说就是:

每一个interpreter进程,只能同时仅有一个线程来执行, 获得相关的锁, 存取相关的资源. 那么很容易就会发现,如果一个interpreter进程只能有一个线程来执行,多线程的并发则成为不可能, 即使这几个线程之间不存在资源的竞争.

从理论上讲,我们要尽可能地使程序更加并行, 能够充分利用多核的功能.

对比一下dict中items与iteritems?

>>> D = {'a':1,'b':2,'c':3,'d':4}    
>>> D.items()                       #一次性取出所有    
[('a', 1), ('c', 3), ('b', 2), ('d', 4)]    
>>> D.iteritems()                   #迭代对象,每次取出一个。用for循环遍历出来;    
<dictionary-itemiterator object at 0x00000000026243B8>    
>>> for i in D.iteritems():    
...   print i,    
...    
('a', 1) ('c', 3) ('b', 2) ('d', 4)    
>>> for k,v in D.iteritems():    
...   print k,    
...    
a c b d    

总结:

  1. 一般iteritems()迭代的办法比items()要快,特别是数据库比较大时。
  2. 在Python3中一般取消前者函数

是否遇到过python的模块间循环引用的问题,如何避免它?

这是代码结构设计的问题,模块依赖和类依赖

如果老是觉得碰到循环引用,很可能是模块的分界线划错地方了。可能是把应该在一起的东西硬拆开了,可能是某些职责放错地方了,可能是应该抽象的东西没抽象

总之微观代码规范可能并不能帮到太多,重要的是更宏观的划分模块的经验技巧,推荐uml,脑图,白板等等图形化的工具先梳理清楚整个系统的总体结构和职责分工

采取办法,从设计模式上来规避这个问题,比如:

  1. 使用 “all” 白名单开放接口
  2. 尽量避免 import

有用过with statement吗?它的好处是什么?

>>> with open('text.txt') as myfile:    
...   while True:    
...     line = myfile.readline()    
...     if not line:    
...       break    
...     print line,    
    
# with语句使用所谓的上下文管理器对代码块进行包装,允许上下文管理器实现一些设置和清理操作。    
# 例如:文件可以作为上下文管理器使用,它们可以关闭自身作为清理的一部分。    
# NOTE:在PYTHON2.5中,需要使用from __future__ import with_statement进行with语句的导入   

用Python生成指定长度的斐波那契数列

def fibs(x):  
    result = [0, 1]  
    for index in range(x-2):  
        result.append(result[-2]+result[-1])  
    return result  
  
if __name__=='__main__':  
    num = input('Enter one number: ')  
    print fibs(num)  

Python里如何生产随机数

>>> import random  
>>> random.random()  
0.29495314937268713  
>>> random.randint(1,11)  
8  
>>> random.choice(range(11))  
3  

Python里如何反序的迭代一个序列

如果是一个list, 最快的解决方案是:

list.reverse()  
try:  
    for x in list:  
        “do something with x”  
finally:  
    list.reverse()  
  
如果不是list, 最通用但是稍慢的解决方案是:  
for i in range(len(sequence)-1, -1, -1):  
x = sequence[i] 

Python中如何定义一个函数

def func(arg, *args, **kwagrs):   #普通函数  
    func_body  
    return   
  
lambda x: x **2                   #匿名函数 

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

import re  
s = ‘<html><head><title>Title</title>’  
print(re.match(‘<.*>’, s).group())  
  
会返回一个匹配<html><head><title>Title</title>而不是<html>  
  
而  
  
import re  
s = ‘<html><head><title>Title</title>’  
print(re.match(‘<.*?>’, s).group())  
  
则会返回<html>  
  
<.*>这种匹配称作贪心匹配 <.*?>称作非贪心匹配 

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

>>> import re  
>>> re.match(r'python','Programing Python, should be pythonic')  
>>> obj1 = re.match(r'python','Programing Python, should be pythonic')  #返回None  
>>> obj2 = re.search(r'python','Programing Python, should be pythonic') #找到pythonic  
>>> obj2.group()  
'python'  
#re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;  
#re.search匹配整个字符串,直到找到一个匹配。  

Python程序中文输出问题怎么解决

在Python3中,对中文进行了全面的支持,但在Python2.x中需要进行相关的设置才能使用中文。否则会出现乱码。

Python默认采取的ASCII编码,字母、标点和其他字符只使用一个字节来表示,但对于中文字符来说,一个字节满足不了需求。

为了能在计算机中表示所有的中文字符,中文编码采用两个字节表示。如果中文编码和ASCII混合使用的话,就会导致解码错误,从而才生乱码。

解决办法:

  • 交互式命令中:一般不会出现乱码,无需做处理
  • py脚本文件中:跨字符集必须做设置,否则乱码

1.首先在开头一句添加:

# coding = utf-8    
# 或    
# coding = UTF-8    
# 或    
# -*- coding: utf-8 -*-   

2.其次需将文件保存为UTF-8的格式!

3.最后: s.decode('utf-8').encode('gbk')

什么是lambda函数

函数使用:

  1. 代码块重复,这时候必须考虑到函数,降低程序的冗余度
  2. 代码块复杂,这时候必须考虑到函数,降低程序的复杂度
  3. Python有两种函数,一种是def定义,一种是lambda函数()

当程序代码很短,且该函数只使用一次,为了程序的简洁,及节省变量内存占用空间,引入了匿名函数这个概念

>>> nums = range(2,20)  
>>> for i in nums:  
        nums = filter(lambda x:x==i or x % i,nums)  
>>> nums  
[2, 3, 5, 7, 11, 13, 17, 19]  

请写出一段Python代码实现删除一个list里面的重复元素

>>> L1 = [4,1,3,2,3,5,1]  
>>> L2 = []  
>>> [L2.append(i) for i in L1 if i not in L2]  
>>> print L2  
[4, 1, 3, 2, 5]  

Python是如何进行类型转换的

>>> int('1234')                   # 将数字型字符串转为整形  
1234  
>>> float(12)                     # 将整形或数字字符转为浮点型  
12.0  
>>> str(98)                       # 将其他类型转为字符串型  
'98'  
>>> list('abcd')                  # 将其他类型转为列表类型  
['a', 'b', 'c', 'd']  
>>> dict.fromkeys(['name','age']) # 将其他类型转为字典类型  
{'age': None, 'name': None}  
>>> tuple([1, 2, 3, 4])           # 将其他类型转为元祖类型  
(1, 2, 3, 4)  

详细转换总结如下:

函数 描述
int(x [,base]) 将x转换为一个整数
long(x [,base] ) 将x转换为一个长整数
float(x) 将x转换到一个浮点数
complex(real [,imag]) 创建一个复数
str(x) 将对象 x 转换为字符串
repr(x) 将对象 x 转换为表达式字符串
eval(str) 用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s) 将序列 s 转换为一个元组
list(s) 将序列 s 转换为一个列表
set(s) 转换为可变集合
dict(d) 创建一个字典。d 必须是一个序列 (key,value)元组。
frozenset(s) 转换为不可变集合
chr(x) 将一个整数转换为一个字符
unichr(x) 将一个整数转换为Unicode字符
ord(x) 将一个字符转换为它的整数值
hex(x) 将一个整数转换为一个十六进制字符串
oct(x) 将一个整数转换为一个八进制字符串

如何知道一个Python对象的类型

>>> type([]);type('');type(0);type({});type(0.0);type((1,))  
<type 'list'>  
<type 'str'>  
<type 'int'>  
<type 'dict'>  
<type 'float'>  
<type 'tuple'>  

Python里面如何拷贝一个对象

切片S[:]  # 注不能应用于字典  
深浅拷贝  # 能应用于所有序列和字典  
  1. 浅拷贝D.copy()方法
  2. 深拷贝deepcopy(D)方法

Python中pass语句的作用是什么

pass语句什么也不做,一般作为占位符或者创建占位程序

写一段程序逐行读入一个文本文件,并在屏幕上打印出来

f = open(filename)    
while True:    
    line = f.readline()    
    if not line: break    
    print(line)    
f.close()   

如何用Python删除一个文件

import os  
os.remove(filename)  

Python代码得到列表list的交集与差集

>>> list1 = [1, 3, 4, 6]  
>>> list2 = [1, 2, 3, 4]  
>>> [i for i in list1 if i not in list2]  
[6]  
>>> [i for i in list1 if i in list2]  
[1, 3, 4]  

Python是如何进行内存管理的

python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创建了一个引用计数,当对象不再需要时,这个对象的引用计数为0时,它被垃圾回收。所有这些都是自动完成,不需要像C一样,人工干预,从而提高了程序员的效率和程序的健壮性。

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

>>> range(10)  
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]  
>>> range(1, 10)  
[1, 2, 3, 4, 5, 6, 7, 8, 9]  
>>> range(0, 9, 2)  
[0, 2, 4, 6, 8]  
>>> range(99,0,-10)  
[99, 89, 79, 69, 59, 49, 39, 29, 19, 9]  

相区别的是xrange(),每次只取出一个迭代对象,如果是数据量比较大时,效率较高

在Python3中,没有xrange()函数,其功能放在了range()函数上

Python异常处理

程序中出现异常情况时就需要异常处理。比如当你打开一个不存在的文件时。当你的程序中有一些无效的语句时,Python会提示你有错误存在。下面是一个拼写错误的例子,print写成了Print

下面是异常最常见的几种角色

1. 错误处理  
>>>可以在程序代码中捕捉和相应错误,或者忽略已发生的异常。  
>>>如果忽略错误,PYTHON默认的异常处理行为将启动:停止程序,打印错误信息。  
>>>如果不想启动这种默认行为,就用try语句来捕捉异常并从异常中恢复。  
2. 事件通知  
>>>异常也可用于发出有效状态的信号,而不需在程序间传递结果标志位。或者刻意对其进行测试  
3. 特殊情况处理  
>>>有时,发生了某种很罕见的情况,很难调整代码区处理。通常会在异常处理中处理,从而省去应对特殊情况的代码  
4. 终止行为  
>>>try/finally语句可确保一定会进行需要的结束运算,无论程序是否有异常  
5. 非常规控制流程 

介绍一下Python中的filter方法

filter就像map,reduce,apply,zip等都是内置函数,用C语言实现,具有速度快,功能强大等

优点。

用于过滤与函数func()不匹配的值, 类似于SQL中select value != 'a'

相当于一个迭代器,调用一个布尔函数func来迭代seq中的每个元素,返回一个是bool_seq返回为True的序列

>>>第一个参数: function or None, 函数或None  
>>>第二个参数: sequence,序列  

介绍一下except的用法和作用

try/except:          捕捉由PYTHON自身或写程序过程中引发的异常并恢复  
except:              捕捉所有其他异常  
except name:         只捕捉特定的异常  
except name, value:  捕捉异常及格外的数据(实例)  
except (name1,name2) 捕捉列出来的异常  
except (name1,name2),value: 捕捉任何列出的异常,并取得额外数据  
else:                如果没有引发异常就运行  
finally:             总是会运行此处代码 

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

>>> words = 'Python is a very funny language!'  
>>> words.find('Python')             # 返回的为0或正数时,为其索引号  
0  
>>> words.find('is')  
7  
>>> words.find('dafa')               # 返回-1表示查找失败  
-1  
>>> words.replace('Python', 'Perl')  # replace()替换  
'Perl is a very funny language!' 

Python如何copy一个文件

import shutil  
shutil.copyfile('a.py', 'copy_a.py') 

Python判断当前用户是否是root

import os  
if os.getuid() != 0:    # root账号的uid=0  
    print os.getuid()  
    print 'Should run as root account'  
else:  
    print 'Hello, Root!' 

用Python写一个for循环的例子

for循环可以遍历序列(列表,字符串,元祖),range()及迭代对象,如xrange()

names = ['Alice', 'Betty', 'Fred', 'Tom']  
for index, name in enumerate(names):  
    print 'index:',index,'=>', name  
  
# 输出结果    
index: 0 => Alice  
index: 1 => Betty  
index: 2 => Fred  
index: 3 => Tom  

介绍一下Python中webbrowser的用法

  • webbrowser模块提供了一个高级接口来显示基于Web的文档,大部分情况下只需要简单的调用open()方法。
  • webbrowser定义了如下的异常:exception webbrowser.Error, 当浏览器控件发生错误是会抛出这个异常

webbrowser有以下方法:

webbrowser.open(url[, new=0[, autoraise=1]])  

这个方法是在默认的浏览器中显示url, 如果new = 0, 那么url会在同一个浏览器窗口下打开,如果new = 1, 会打开一个新的窗口,如果new = 2, 会打开一个新的tab, 如果autoraise = true, 窗口会自动增长。

webbrowser.open_new(url)  

在默认浏览器中打开一个新的窗口来显示url, 否则,在仅有的浏览器窗口中打开url

webbrowser.open_new_tab(url)  

在默认浏览器中当开一个新的tab来显示url, 否则跟open_new()一样

webbrowser.get([name]) 

根据name返回一个浏览器对象,如果name为空,则返回默认的浏览器

webbrowser.register(name, construtor[, instance])  

注册一个名字为name的浏览器,如果这个浏览器类型被注册就可以用get()方法来获取。

默写尽可能多的str对象的方法

方法 描述
S.capitalize() 返回首字母大写的字符串的副本
S.center(width[,fillchar]) 返回一个长度为max(len(S),width),S居中,两侧fillchar填充
S.count(sub[,start[,end]]) 计算子字符串sub的出现次数,可将搜索范围限制为S[start:end]
S.decode([encoding[,error]]) 返回使用给定编码方式的字符串的解码版本,由error指定错误处理方式
S.endswith(suffix[start[,end]]) 检查S是否以suffix结尾,可给定[start:end]来选择匹配的范围
S.expandtabs([tabsize]) 返回字符串的副本,其中tab字符会使用空格进行扩展,可选择tabsize
S.find(sun[,start[,end]]) 返回子字符串sub的第一个索引,不存在则为-1,可选择搜索范围
S.index(sub[,start[,end]]) 返回子字符串sub的第一个索引,不存在则引发ValueError异常.
S.isalnum() 检查字符串是否由字母或数字字符组成
S.isalpha() 检查字符串是否由字母字符组成
S.isdigit() 检查字符串是否由数字字符组成
S.islower() 检查字符串是否由小写字母组成
S.isspace() 检查字符串是否由空格组成
S.istitle() 检查字符串时候首字母大写
S.isupper() 检查字符串是否由大写字母组成
S.join(sequence) 返回其中sequence的字符串元素由S连接的字符串
S.ljust(width[,fillchar]) 返回S副本左对齐的字符串,长度max(len(S),W),右侧fillchar填充
S.lower() 返回所有字符串都为小写的副本
S.lstrip([char]) 向左移除所有char,默认移除(空格,tab,\n)
S.partition(seq) 在字符串中搜索seq并返回
S.replace(old,new[,max]) 将new替换olad,最多可替换max次
S.rfind(sub[,start[,end]]) 返回sub所在的最后一个索引,不存在则为-1,可定搜索范围S[start:end]
S.rindex(sub[,start[,end]]) 返回sub所在的最后一个索引,不存在则会引发ValueError异常。
S.rjust(width[,fillchar]) 返回S副本右对齐的字符串,长度max(len(S),W),左侧fillchar填充
S.rpartition(seq) 同Partition,但从右侧开始查找
S.rstip([char]) 向右移除所有char,默认移除(空格,tab,\n)
S.rsplit(sep[,maxsplit]) 同split,但是使用maxsplit时是从右往左进行计数
S.split(sep[,maxsplit]) 使用sep做为分割符,可使用maxsplit指定最大切分数
S.zfill(width) 在S的左侧以0填充width个字符
S.upper() 返回S的副本,所有字符大写
S.splitlines([keepends]) 返回S中所有行的列表,可选择是否包括换行符
S.startswith(prefix[,start[,end]]) 检查S是否以prefix开始,可用[start,end]来定义范围
S.strip([chars]) 移除所有字符串中含chars的字符,默认移除(空格,tab,\n)
S.swapcase() 返回S的副本,所有大小写交换
S.title() 返回S的副本,所有单词以大写字母开头
S.translate(table[,deletechars]) 返回S的副本,所有字符都使用table进行的转换,可选择删除出现在deletechars中的所有字符

现在有一个dict对象adict,里面包含了一百万个元素,查找其中的某个元素的平均需要多少次比较

O(1) 哈希字典,快速查找,键值映射,键唯一!

有一个list对象alist,里面的所有元素都是字符串,编写一个函数对它实现一个大小写无关的排序

words = ['This','is','a','dog','!']  
words.sort(key=lambda x:x.lower())  
print words  
#输出结果  
>>>   
['!', 'a', 'dog', 'is', 'This']  

有一个排好序地list对象alist,查找其中是否有某元素a

alist = ['a','s','d','f']  
  
try:  
    alist.index('a')  
    print 'Find it.'  
except ValueError:  
    print 'Not Found.'  
 ```
 
## 请用Python写一个获取用户输入数字,并根据数字大小输出不同信息的脚本

```python
num = input('Enter number: ')  
  
if num > 100:  
    print 'The number is over 100'  
elif 0 < num <= 100:  
    print 'The number is between 0~100'  
elif num < 0:  
    print 'The number is negative.'  
else:  
    print 'Not a number'  

打乱一个排好序的list对象alist

# random模块中的shuffle(洗牌函数)  
import random  
alist = [1, 2, 3, 4]  
random.shuffle(alist)     
print alist 

有二维的list对象alist,假定其中的所有元素都具有相同的长度,写一段程序根据元素的第二个元素排序

def sort_lists(lists, sord, idx):  
    if sord == 'desc':  
        lists.sort(key=lambda x:x[idx], reverse=True)  
    else:  
        lists.sort(key=lambda x:x[idx])  
    return lists  
lists = [['cd','ab'],['ef','ac']]  
sort_lists(lists,'desc',1)  
print lists  
  
# 输出结果  
>>>   
[['ef', 'ac'], ['cd', 'ab']]  

inspect模块有什么用

inspect模块提供了一系列函数用于帮助使用自省。

检查对象类型

is{module|class|function|method|builtin}(obj): 检查对象是否为模块、类、函数、方法、内建函数或方法。  
isroutine(obj): 用于检查对象是否为函数、方法、内建函数或方法等等可调用类型。  

获取对象信息

getmembers(object[, predicate])
这个方法是dir()的扩展版,它会将dir()找到的名字对应的属性一并返回。  

getmodule(object)
它返回object的定义所在的模块对象。  

get{file|sourcefile}(object)
获取object的定义所在的模块的文件名|源代码文件名(如果没有则返回None)。  

get{source|sourcelines}(object)
获取object的定义的源代码,以字符串|字符串列表返回。  

getargspec(func)
仅用于方法,获取方法声明的参数,返回元组,分别是(普通参数名的列表, *参数名, **参数名, 默认值元组)。  

Python处理命令行参数示例代码

# 最简单、最原始的方法就是手动解析了  
import sys  
for arg in sys.argv[1:]:  
    print(arg) 

介绍一下Python getopt模块

# getopt模块是原来的命令行选项解析器,支持UNIX函数getopt()建立的约定。  
# 它会解析一个参数序列,如sys.argv,并返回一个元祖序列和一个非选项参数序列。  
# 目前支持的选项语法包括短格式和长格式选项:-a, -bval, -b val, --noarg, --witharg=val, --witharg val。  
# 如果只是简单的命令行解析,getopt还是不错的选择。一个例子如下  
  
import sys  
import getopt  
  
try:  
    options, remainder = getopt.getopt(sys.argv[1:], 'o:v', ['output=', 'verbose', 'version=',])  
except getopt.GetoptError as err:  
    print 'ERROR:', err  
    sys.exit(1)  

总结下getopt的特点:

  1. getopt是从前到后解析
  2. getopt不检查额外参数的合法性,需要自行检查
  3. 短命令行和长命令行是分开解析的

有一个长度是101的数组,存在1~100的数字,有一个是重复的,拿重复的找出来

# Python中,主要是拿count(i) ==2的找出来即可,再利用列表推导式  
>>> l = [1, 2, 3, 4, 2]  
>>> tmp = []  
>>> [tmp.append(i) for i in l if l.count(i) == 2]  
[None, None]  
>>> tmp  
[2, 2]  
>>> set(tmp)  
set([2])  

set是在哪个版本成为build-in types的?举例说明,并说明为什么当时选择了set这种数据结构

python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和sysmmetric difference(对称差集)等数学运算.

sets 支持 x in set, len(set),和 for x in set。作为一个无序的集合,sets不记录元素位置或者插入点。因此,sets不支持 indexing, slicing, 或其它类序列(sequence-like)的操作。

下面来点简单的小例子说明。

>>> x = set('spam')  
>>> y = set(['h','a','m'])  
>>> x, y  
(set(['a', 'p', 's', 'm']), set(['a', 'h', 'm']))  

再来些小应用。

>>> x & y # 交集  
set(['a', 'm'])  
  
>>> x | y # 并集  
set(['a', 'p', 's', 'h', 'm'])  
  
>>> x - y # 差集  
set(['p', 's'])  

去除海量列表里重复元素,用hash来解决也行,只不过感觉在性能上不是很高,用set解决还是很不错的,示例如下:

>>> a = [11,22,33,44,11,22]  
>>> b = set(a)  
>>> b  
set([33, 11, 44, 22])  
>>> c = [i for i in b]  
>>> c  
[33, 11, 44, 22]  

集合

集合用于包含一组无序的对象。要创建集合,可使用set()函数并像下面这样提供一系列的项:

s = set([3,5,9,10])      #创建一个数值集合  
t = set("Hello")         #创建一个唯一字符的集合  

与列表和元组不同,集合是无序的,也无法通过数字进行索引。此外,集合中的元素不能重复。例如,如果检查前面代码中t集合的值,结果会是:

>>> t  
set(['H', 'e', 'l', 'o'])  
注意只出现了一个'l'。  

集合支持一系列标准操作,包括并集、交集、差集和对称差集,例如:

a = t | s          # t 和 s的并集  
b = t & s          # t 和 s的交集  
c = t – s         # 求差集(项在t中,但不在s中)  
d = t ^ s          # 对称差集(项在t或s中,但不会同时出现在二者中)  

基本操作:

t.add('x')            # 添加一项  
s.update([10,37,42])  # 在s中添加多项  

Python如何实现单例模式

#-*- encoding=utf-8 -*-  
print '----------------------方法1--------------------------'  
#方法1,实现__new__方法  
#并在将一个类的实例绑定到类变量_instance上,  
#如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回  
#如果cls._instance不为None,直接返回cls._instance  
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._instance  
  
class MyClass(Singleton):  
    a = 1  
  
one = MyClass()  
two = MyClass()  
  
two.a = 3  
print one.a  
#3  
#one和two完全相同,可以用id(), ==, is检测  
print id(one)  
#29097904  
print id(two)  
#29097904  
print one == two  
#True  
print one is two  
#True  
  
print '----------------------方法2--------------------------'  
#方法2,共享属性;所谓单例就是所有引用(实例、对象)拥有相同的状态(属性)和行为(方法)  
#同一个类的所有实例天然拥有相同的行为(方法),  
#只需要保证同一个类的所有实例具有相同的状态(属性)即可  
#所有实例共享属性的最简单最直接的方法就是__dict__属性指向(引用)同一个字典(dict)  
#可参看:http://code.activestate.com/recipes/66531/  
class Borg(object):  
    _state = {}  
    def __new__(cls, *args, **kw):  
        ob = super(Borg, cls).__new__(cls, *args, **kw)  
        ob.__dict__ = cls._state  
        return ob  
  
class MyClass2(Borg):  
    a = 1  
  
one = MyClass2()  
two = MyClass2()  
  
#one和two是两个不同的对象,id, ==, is对比结果可看出  
two.a = 3  
print one.a  
#3  
print id(one)  
#28873680  
print id(two)  
#28873712  
print one == two  
#False  
print one is two  
#False  
#但是one和two具有相同的(同一个__dict__属性),见:  
print id(one.__dict__)  
#30104000  
print id(two.__dict__)  
#30104000  
  
print '----------------------方法3--------------------------'  
#方法3:本质上是方法1的升级(或者说高级)版  
#使用__metaclass__(元类)的高级python用法  
class Singleton2(type):  
    def __init__(cls, name, bases, dict):  
        super(Singleton2, cls).__init__(name, bases, dict)  
        cls._instance = None  
    def __call__(cls, *args, **kw):  
        if cls._instance is None:  
            cls._instance = super(Singleton2, cls).__call__(*args, **kw)  
        return cls._instance  
  
class MyClass3(object):  
    __metaclass__ = Singleton2  
  
one = MyClass3()  
two = MyClass3()  
  
two.a = 3  
print one.a  
#3  
print id(one)  
#31495472  
print id(two)  
#31495472  
print one == two  
#True  
print one is two  
#True  
  
print '----------------------方法4--------------------------'  
#方法4:也是方法1的升级(高级)版本,  
#使用装饰器(decorator),  
#这是一种更pythonic,更elegant的方法,  
#单例类本身根本不知道自己是单例的,因为他本身(自己的代码)并不是单例的  
def singleton(cls, *args, **kw):  
    instances = {}  
    def _singleton():  
        if cls not in instances:  
            instances[cls] = cls(*args, **kw)  
        return instances[cls]  
    return _singleton  
 
@singleton  
class MyClass4(object):  
    a = 1  
    def __init__(self, x=0):  
        self.x = x  
  
one = MyClass4()  
two = MyClass4()  
  
two.a = 3  
print one.a  
#3  
print id(one)  
#29660784  
print id(two)  
#29660784  
print one == two  
#True  
print one is two  
#True  
one.x = 1  
print one.x  
#1  
print two.x  
#1  

如何用Python来发送邮件

# 可以使用smtplib标准库。  
# 以下代码可以在支持SMTP监听器的服务器上执行。  
  
import sys, smtplib  
  
fromaddr = raw_input("From: ")  
toaddrs  = raw_input("To: ").split(',')  
print("Enter message, end with ^D:")  
msg = ''  
while 1:  
    line = sys.stdin.readline()  
    if not line:  
        break  
msg += line  
  
# 发送邮件部分  
server = smtplib.SMTP('localhost')  
server.sendmail(fromaddr, toaddrs, msg)  
server.quit()  
【题目:055】| Python自动连接ssh的代码
#!/usr/bin/env python  
  
#import the need library.  
import pxssh  
  
#machine details  
hostname = ''  
username = ''  
password = ''  
  
#command we want to send  
command = 'ls -lart'  
  
#function to connect  
def connect(hostname, username, password, release):  
    try:  
        s = pxssh.pxssh()  
        s.login(hostname, username, password, release)  
        print s  
        return s  
    except Exception, e:  
        print "[-] Error Connecting: " + str(e)  
  
#func to send a command  
def send_command(ssh_session, command):  
    ssh_session.sendline(command)  
    ssh_session.prompt()  
    print ssh_session.before  
  
#main()  
if __name__ == "__main__":  
    session = connect(hostname, username, password)  
    send_command(session, command)  

或者用pexpect模块

#!/usr/bin/env python  
  
import pexpect  
  
def ssh_cmd(ip, passwd, cmd):  
    ret = -1  
    ssh = pexpect.spawn('ssh root@%s "%s"' % (ip, cmd))  
    try:  
        i = ssh.expect(['password:', 'continue connecting (yes/no)?'], timeout=5)  
        if i == 0 :  
            ssh.sendline(passwd)  
        elif i == 1:  
            ssh.sendline('yes\n')  
            ssh.expect('password: ')  
            ssh.sendline(passwd)  
        ssh.sendline(cmd)  
        res = ssh.read()  
        print res  
        ret = 0  
    except pexpect.EOF:  
        print "EOF"  
        ssh.close()  
        ret = -1  
    except pexpect.TIMEOUT:  
        print "TIMEOUT"  
        ssh.close()  
        ret = -2  
    return ret  
  
#main()  
if __name__ == "__main__":  
    ssh_cmd('127.0.0.1', 'password', 'ls -lart')  
 ```
 
## 介绍一下Python Date Time方面的类

```python
一.time模块  
time模块提供各种操作时间的函数  
一般有两种表示时间的方式:  
第一种: 是时间戳的方式(相对于1970.1.1 00:00:00以秒计算的偏移量),时间戳是惟一的  
第二种: 以数组的形式表示即(struct_time),共有九个元素,分别表示,同一个时间戳的struct_time会因为时区不同而不同  
  
二.datetime模块  
Python提供了多个内置模块用于操作日期时间,像calendar,time,datetime。time模块。  
相比于time模块,datetime模块的接口则更直观、更容易调用。  
datetime模块定义了下面这几个类:  
datetime.date:表示日期的类。常用的属性有year, month, day;  
datetime.time:表示时间的类。常用的属性有hour, minute, second, microsecond;  
datetime.datetime:表示日期时间。  
datetime.timedelta:表示时间间隔,即两个时间点之间的长度。  
datetime.tzinfo:与时区有关的相关信息。  
datetime中,表示日期时间的是一个datetime对象  
datetime中提供了strftime方法,可以将一个datetime型日期转换成字符串

写一个简单的Python socket编程

服务器端程序:

# FileName: server.py  
  
import socket  
  
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
sock.bind(('localhost', 8001))  
  
sock.listen(5)  
while True:  
    conn, addr = sock.accept()  
    try:  
        conn.settimeout(5)  
        buff = conn.recv(1024)  
        if buff == '1':  
            conn.send('Hello, Client...')  
        else:  
            conn.send('Please, Go Out...')  
    except socket.timeout:  
        print 'Socket Time Out...'  
    finally:  
        conn.close()  

客户端程序:

# FileName: client.py  
import socket  
import time  
  
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
sock.connect(('localhost', 8001))  
time.sleep(2)  
sock.send('1')  
print sock.recv(1024)  
sock.close() 

在终端运行server.py,然后运行clien.py,会在终端打印“Hello, Client..."。
如果更改client.py的sock.send('1')为其它值在终端会打印“Please, Go Out...”。
更改time.sleep(2)为大于5的数值, 服务器将会超时。

解释一下python的and-or语法

0 and * 不需要再考虑*是0还是1,结果是0

1 and * 需要考虑*是0还是1来决定结果。

1 or * 不需要考虑后面的*,结果为1

0 or * 需要考虑后面的*来决定结果

这个语法看起来类似于 C 语言中的 bool ? a : b 表达式。整个表达式从左到右进行演算,所以先进行 and 表达式的演算。 1 and 'first' 演算值为 'first',然后 'first' or 'second' 的演算值为 'first'。

0 and 'first' 演算值为 False,然后 0 or 'second' 演算值为 'second'。

and-or主要是用来模仿 三目运算符 bool?a:b的,即当表达式bool为真,则取a否则取b。

and-or 技巧,bool and a or b 表达式,当 a 在布尔上下文中的值为假时,不会像 C 语言表达式 bool ? a : b 那样工作。

Python里关于“堆”这种数据结构的模块是哪个?“堆”有什么优点和缺点

这个真没有!

实现一个stack

class Stack :  
    def __init__( self ):  
        ''''' Creates an empty stack. '''  
        self._items = list()  
          
    def isEmpty(self):  
        ''''' Returns True if the stack is empty or False otherwise. '''  
        return len(self) == 0  
  
    def __len__(self):  
        ''''' Returns the number of items in the stack. '''  
        return len(self._items)  
     
    def peek(self):  
       ''''' Returns the top item on the stack without removing it. '''  
       assert not self.isEmpty(), "Cannot peek at an empty stack"  
       return self._items[-1]  
  
    def pop(self):  
        ''''' Removes and returns the top item on the stack. '''  
        assert not self.isEmpty(), "Cannot pop from an empty stack"  
        return self._items.pop()  
     
    def push(self,item):  
        ''''' Push an item onto the top of the stack. '''  
        self._items.append( item )  
  ```
  
## 编写一个简单的ini文件解释器

db_config.ini

[baseconf]
host=127.0.0.1
port=3306
user=root
password=root
dbname=evalutingsys
[concurrent]
processor=20
```

示例代码

import sys,os
import ConfigParser
def test(config_file_path):
    cf = ConfigParser.ConfigParser()
    cf.read(config_file_path)

    s = cf.sections()
    print 'section:', s

    o = cf.options("baseconf")
    print 'options:', o

    v = cf.items("baseconf")
    print 'db:', v

    db_host = cf.get("baseconf", "host")
    db_port = cf.getint("baseconf", "port")
    db_user = cf.get("baseconf", "user")
    db_pwd = cf.get("baseconf", "password")

    print db_host, db_port, db_user, db_pwd

    cf.set("baseconf", "db_pass", "123456")
    cf.write(open("config_file_path", "w"))
if __name__ == "__main__":
    test("../conf/db_config.ini")

Tuple 和 List 的转换

函数tuple(seq)可以把所有可迭代的(iterable)序列转换成一个tuple, 元素不变,排序也不变。

例如,tuple([1,2,3])返回(1,2,3), tuple(‘abc’)返回(‘a’.'b’,'c’).如果参数已经是一个tuple的话,函数不做任何拷贝而直接返回原来的对象,所以在不确定对象是不是tuple的时候来调用tuple()函数也不是很耗费的。

函数list(seq)可以把所有的序列和可迭代的对象转换成一个list,元素不变,排序也不变。

例如 list([1,2,3])返回(1,2,3), list(‘abc’)返回['a', 'b', 'c']。如果参数是一个list, 她会像set[:]一样做一个拷贝

python下多线程的限制以及多进程中传递参数的方式

python多线程有个全局解释器锁(global interpreter lock),这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不是“并行”。

多进程间共享数据,可以使用 multiprocessing.Value 和 multiprocessing.Array

Python是如何进行内存管理的?

Python引用了一个内存池(memory pool)机制,即Pymalloc机制(malloc:n.分配内存),用于管理对小块内存的申请和释放

内存池(memory pool)的概念:

当创建大量消耗小内存的对象时,频繁调用new/malloc会导致大量的内存碎片,致使效率降低。内存池的概念就是预先在内存中申请一定数量的,大小相等 的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够了之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。

内存池的实现方式有很多,性能和适用范围也不一样。

python中的内存管理机制——Pymalloc:
  
python中的内存管理机制都有两套实现,一套是针对小对象,就是大小小于256bits时,pymalloc会在内存池中申请内存空间;当大于256bits,则会直接执行new/malloc的行为来申请内存空间。

关于释放内存方面,当一个对象的引用计数变为0时,python就会调用它的析构函数。在析构时,也采用了内存池机制,从内存池来的内存会被归还到内存池中,以避免频繁地释放动作。

什么是lambda函数?它有什么好处?

lambda 函数是一个可以接收任意多个参数(包括可选参数)并且返回单个表达式值的函数。 lambda 函数不能包含命令,它们所包含的表达式不能超过一个。不要试图向lambda 函数中塞入太多的东西;如果你需要更复杂的东西,应该定义一个普通函数,然后想让它多长就多长。

列表与元组的区别是什么.分别在什么情况下使用

列表中的项目应该包括在方括号中,你可以添加、删除或是搜索列表中的项目。由于你可以增加或删除项目,所以列表是可变的数据类型,即这种类型是可以被改变的。

元组和列表十分类似,但是元组是不可变的.也就是说你不能修改元组。元组通过圆括号中用逗号分割的项目定义。元组通常用在使语句或用户定义的函数能够安全地采用一组值的时候,即被使用的元组的值不会改变。

字典

键值对的集合(map)字典是以大括号“{}”包围的数据集合。

与列表区别:字典是无序的,在字典中通过键来访问成员。字典是可变的,可以包含任何其他类型。

说明os,sys模块不同,并列举常用的模块方法?

官方解释:

os: This module provides a portable way of using operating system dependent functionality.

翻译:提供一种方便的使用操作系统函数的方法。

sys:This module provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter.

翻译:提供访问由解释器使用或维护的变量和在与解释器交互使用到的函数。