面试题

https://github.com/taizilongxu/interview_python#2-python%E4%B8%AD%E7%9A%84%E5%85%83%E7%B1%BBmetaclass

1. 装饰器(什么是AOP/面向切面编程)

装饰器并不是一个功能特性,只是一个语法糖;因为python装饰器的本质是将函数或对象作为参数传递给另一个函数或对象,最后再返回新的函数对象,这是一个早就有的概念,装饰器只是这个旧概念的新语法。

面向切面编程(AOP)是一种编程思想,与OOP并不矛盾,只是它们的关注点相同。面向对象的目的在于抽象和管理,而面向切面的目的在于解耦和复用。

 

2. python装饰器的wraps作用

Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring

 

3. 迭代器与生成器的区别什么

字符串、列表、元祖、集合、字典都是可迭代的,数字是不可迭代的。(可以用for循环遍历取出内部元素的就是可迭代的)

迭代器:用变量调__iter__后就可以生成一个迭代器,迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。

(2)生成器是可以迭代的,但是只可以读取它一次

(3)生成器(generator)能够迭代的关键是他有next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。

(4)带有yield的函数不再是一个普通的函数,而是一个生成器generator,可用于迭代

(5)yield是一个类似return 的关键字,迭代一次遇到yield的时候就返回yield后面或者右面的值。而且下一次迭代的时候,从上一次迭代遇到的yield后面的代码开始执行

(6)yield就是return返回的一个值,并且记住这个返回的位置。下一次迭代就从这个位置开始。

(7)带有yield的函数不仅仅是只用于for循环,而且可用于某个函数的参数,只要这个函数的参数也允许迭代参数。

(8)send()和next()的区别就在于send可传递参数给yield表达式,这时候传递的参数就会作为yield表达式的值,而yield的参数是返回给调用者的值,也就是说send可以强行修改上一个yield表达式值。

(9)send()和next()都有返回值,他们的返回值是当前迭代遇到的yield的时候,yield后面表达式的值,其实就是当前迭代yield后面的参数。

(10)第一次调用时候必须先next()或send(),否则会报错,send后之所以为None是因为这时候没有上一个yield,所以也可以认为next()等同于send(None)

4. Python代码执行原理

Python先启动 Python 的解释器,把代码(.py文件)编译成字节码对象PyCodeObject,交给字节码虚拟机,然后解释器一条一条执行字节码指令,从而完成程序的执行。

这个 PyCodeObject 对象包含了 Python 源代码中的字符串,常量值,以及通过语法解析后编译生成的字节码指令。PyCodeObject 对象还存储字节码指令与原始代码行号的对应关系,这样当出现异常时,就能指明位于哪一行的代码

 

5. 解释Python的对象

python一切皆为对象,一个对象的特征称为属性。它所具有的行为称为方法(method)

Python中的对象包含三要素:id、type、value

  • 其中id用来唯一标识一个对象,
  • type标识对象的类型,
  • value是对象的值

is判断的是a对象是否就是b对象,是通过id来判断的

==判断的是a对象的值是否和b对象的值相等,是通过value来判断的

 

6. 什么是GIL、为什么要加GIL、如何理解Python多线程

线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。

解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).

 

7. 什么是协程

简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态.

 

8. Python的IO多路复用是怎么实现的

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。关于这三种IO多路复用的用法,前面三篇总结写的很清楚,并用服务器回射echo程序进行了测试

其实所有的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

 

9. 什么是上下文管理器

上下文管理器的目的就是规定对象的使用范围,一旦进入或者离开该使用范围,会有特殊操作被调用 (比如为对象分配或者释放内存)。它的语法形式是with...as...。

当我们使用上下文管理器的语法时,我们实际上要求Python在进入程序块之前调用对象的__enter__()方法,在结束程序块的时候调用__exit__()方法。对于文件对象f来说,它定义了__enter__()和__exit__()方法(可以通过dir(f)看到)。在f的__exit__()方法中,有self.close()语句。所以在使用上下文管理器时,我们就不用明文关闭f文件了。

 

10. 什么是闭包

当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点:

  1. 必须有一个内嵌函数
  2. 内嵌函数必须引用外部函数中的变量
  3. 外部函数的返回值必须是内嵌函数

 

11. python中一般的类都继承object,那object的父类是什么(type)

 

12. 谈谈元类、元类的应用

道生一,一生二,二生三,三生万物。

  • 道 即是 type
  • 一 即是 metaclass(元类,或者叫类生成器)
  • 二 即是 class(类,或者叫实例生成器)
  • 三 即是 instance(实例)
  • 万物 即是 实例的各种属性与方法,我们平常使用python时,调用的就是它们。

class Foo(Bar):

    pass

元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。好吧,元类就是用来创建这些类(对象)的,元类就是类的类,Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类

class SayMetaClass(type):

    def __new__(cls, name, bases, attrs):

        attrs['say_'+name] = lambda self,value,saying=name:

print(saying+','+value+'!')

        return type.__new__(cls, name, bases, attrs)

1)元类是由type衍生而出,所以父类需要传入type。【道生一,所以一必须包含道】

2)元类的操作都在 __new__中完成,它的第一个参数是将创建的类,之后的参数即是三大永恒命题:我是谁,我从哪里来,我将到哪里去。 它返回的对象也是三大永恒命题,接下来,这三个参数将一直陪伴我们。

 

13. 多重继承

MRO:Method Resolution Order,即方法解析顺序,是python中用于处理二义性问题的算法

如果是经典类,MRO使用DFS

如果是新式类,MRO使用BFS

二义性:python支持多继承,多继承的语言往往会遇到以下两类二义性的问题:

1) 有两个基类A和B,A和B都定义了方法f(),C继承A和B,那么调用C的f()方法时不确定。

2) 有一个基类A,定义了方法f(),B类和C类继承了A类(的f()方法),D类继承了B和C类,那么出现一个问题,D不知道应该继承B的f()方法还是C的f()方法。

队列先进先出,栈先进后出

  • 先序遍历:对任一子树,先访问根,然后遍历其左子树,最后遍历其右子树。
  • 中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树。
  • 后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根
  • 深度优先算法(DFS,Depth-First-Search)

把根节点压入栈中。

每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。

找到所要找的元素时结束程序。

如果遍历整个树还没有找到,结束程序。

  • 广度优先算法(BFS,Breadth-First-Search)

把根节点放到队列的末尾。

每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。

找到所要找的元素时结束程序。

如果遍历整个树还没有找到,结束程序。

 

14. 浅复制和深复制有什么区别

(1)直接赋值是一个完完全全的引用,对新变量的任何改动都会影响到原对象。

直接赋值的新变量完完全全就是一个原对象的引用,任何对复制对象的引用都会影响到原对象。

(2)浅拷贝创建了新的对象,但是只拷贝了序列的元素,对于元素也是一个序列的情况(即子对象),只复制了对这个序列的引用!

两个对象指向的内存并不相同,也就是说,浅拷贝的对象是一个新的对象。另外,可以发现,对新对象的元素进行替换并不会影响到原对象,而对子对象——列表的修改会影响到原对象。

(3)深拷贝是完完全全的拷贝,把原对象完整地拷贝到了新对象中。

两个对象指向的内存位置并不相同,说明创建了新对象。此外,新对象的任何改动都不影响到原有的对象

15. __new__和__init__的区别

  • __init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值。
  • __new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例,是个静态方法。
  • __new__是一个静态方法,而__init__是一个实例方法.
  • __new__方法会返回一个创建的实例,而__init__什么都不返回.
  • 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
  • 当创建一个新实例时调用__new__,初始化一个实例时用__init__.

 

16. 单例模式

​ 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

 

 

17. 浏览器输入url后都干了些什么

https://www.linux178.com/web/httprequest.html

域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

1) DNS解析

在进行DNS解析时,会经历以下步骤:

  • 查询浏览器缓存(浏览器会缓存之前拿到的DNS 2-30分钟时间),如果没有找到,
  • 检查系统缓存,检查hosts文件,这个文件保存了一些以前访问过的网站的域名和IP的数据。它就像是一个本地的数据库。如果找到就可以直接获取目标主机的IP地址了。没有找到的话,需要
  • 检查路由器缓存,路由器有自己的DNS缓存,可能就包括了这在查询的内容;如果没有,要
  • 查询ISP DNS 缓存:ISP服务商DNS缓存(本地服务器缓存)那里可能有相关的内容,如果还不行的话,需要,
  • 递归查询:从根域名服务器到顶级域名服务器再到极限域名服务器依次搜索哦对应目标域名的IP。接下来就是向该IP地址定位的HTTP服务器发起TCP连接

2) 浏览器与网站建立TCP连接(三次握手)

第一次握手:客户端向服务器端发送请求(SYN=1) 等待服务器确认;

第二次握手:服务器收到请求并确认,回复一个指令(SYN=1,ACK=1);

第三次握手:客户端收到服务器的回复指令并返回确认(ACK=1)。

3) 发起HTTP请求

通过三次握手,建立了客户端和服务器之间的连接,现在可以请求和传输数据了。

4) 服务器端响应http请求,浏览器得到html代码

5) 浏览器解析html代码

可以将服务器上的目标文件传输到浏览器进行渲染

客户端拿到服务器端传输来的文件,找到HTML和MIME文件,通过MIME文件,浏览器知道要用页面渲染引擎来处理HTML文件。

a.浏览器会解析html源码,然后创建一个 DOM树。

在DOM树中,每一个HTML标签有一个对应的节点,并且每个文本也都会有个对应的文本节点。

b.浏览器解析CSS代码,计算出最终的样式数据,形成css对象模型CSSOM。

首先会忽略非法的CSS代码,之后按照浏览器默认设置——用户设置——外链样式——内联样式——HTML中的style样式顺序进行渲染。

c.利用DOM和CSSOM构建一个渲染树(rendering tree)。

渲染树和DOM树有点像,但是是有区别的。

DOM树完全和html标签一一对应,但是渲染树会忽略掉不需要渲染的元素,比如head、display:none的元素等。

而且一大段文本中的每一个行在渲染树中都是独立的一个节点。

渲染树中的每一个节点都存储有对应的css属性。

d.浏览器就根据渲染树直接把页面绘制到屏幕上。

 

18. 新式类和旧式类

简单的说,新式类是在创建的时候继承内置object对象(或者是从内置类型,如list,dict等),而经典类是直接声明的。使用dir()方法也可以看出新式类中定义很多新的属性和方法。这些新的属性和方法都是从object对象中继承过来的。http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html

 

19. Python中的作用域

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

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

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

 

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

 

21. 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中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。

 

22. read,readline和readlines

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

 

23. Python 2.7.x 与 Python 3.x 的主要差异

 

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

Python2.7中的super方法浅见

 

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

 

26. 调度算法

  1. 先来先服务(FCFS, First Come First Serve)
  2. 短作业优先(SJF, Shortest Job First)
  3. 最高优先权调度(Priority Scheduling)
  4. 时间片轮转(RR, Round Robin)
  5. 多级反馈队列调度(multilevel feedback queue scheduling)

常见的调度算法总结:http://www.jianshu.com/p/6edf8174c1eb

实时调度算法:

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

 

27. 死锁

原因:

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

必要条件:

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

处理死锁基本方法:

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

死锁概念处理策略详细介绍:https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/10.html

 

6 虚拟内存技术

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

7 分页和分段

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

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

分页与分段的主要区别

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

8 页面置换算法

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

9 边沿触发和水平触发

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

 

 

1 事务

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 彻底理解数据库事务: http://www.hollischuang.com/archives/898

2 数据库索引

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

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

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

3 Redis原理

Redis是什么?

  1. 是一个完全开源免费的key-value内存数据库
  2. 通常被认为是一个数据结构服务器,主要是因为其有着丰富的数据结构 strings、map、 list、sets、 sorted sets

Redis数据库

​ 通常局限点来说,Redis也以消息队列的形式存在,作为内嵌的List存在,满足实时的高并发需求。在使用缓存的时候,redis比memcached具有更多的优势,并且支持更多的数据类型,把redis当作一个中间存储系统,用来处理高并发的数据库操作

  • 速度快:使用标准C写,所有数据都在内存中完成,读写速度分别达到10万/20万
  • 持久化:对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上,主要有两种策略,一是根据时间,更新次数的快照(save 300 10 )二是基于语句追加方式(Append-only file,aof)
  • 自动操作:对不同数据类型的操作都是自动的,很安全
  • 快速的主--从复制,官方提供了一个数据,Slave在21秒即完成了对Amazon网站10G key set的复制。
  • Sharding技术: 很容易将数据分布到多个Redis实例中,数据库的扩展是个永恒的话题,在关系型数据库中,主要是以添加硬件、以分区为主要技术形式的纵向扩展解决了很多的应用场景,但随着web2.0、移动互联网、云计算等应用的兴起,这种扩展模式已经不太适合了,所以近年来,像采用主从配置、数据库复制形式的,Sharding这种技术把负载分布到多个特理节点上去的横向扩展方式用处越来越多。

Redis缺点

  • 是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

4 乐观锁和悲观锁

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

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

乐观锁与悲观锁的具体区别: http://www.cnblogs.com/Bob-FD/p/3352216.html

5 MVCC

​ 全称是Multi-Version Concurrent Control,即多版本并发控制,在MVCC协议下,每个读操作会看到一个一致性的snapshot,并且可以实现非阻塞的读。MVCC允许数据具有多个版本,这个版本可以是时间戳或者是全局递增的事务ID,在同一个时间点,不同的事务看到的数据是不同的。

MySQL的innodb引擎是如何实现MVCC的

innodb会为每一行添加两个字段,分别表示该行创建的版本和删除的版本,填入的是事务的版本号,这个版本号随着事务的创建不断递增。在repeated read的隔离级别(事务的隔离级别请看这篇文章)下,具体各种数据库操作的实现:

  • select:满足以下两个条件innodb会返回该行数据:
    • 该行的创建版本号小于等于当前版本号,用于保证在select操作之前所有的操作已经执行落地。
    • 该行的删除版本号大于当前版本或者为空。删除版本号大于当前版本意味着有一个并发事务将该行删除了。
  • insert:将新插入的行的创建版本号设置为当前系统的版本号。
  • delete:将要删除的行的删除版本号设置为当前系统的版本号。
  • update:不执行原地update,而是转换成insert + delete。将旧行的删除版本号设置为当前版本号,并将新行insert同时设置创建版本号为当前版本号。

其中,写操作(insert、delete和update)执行时,需要将系统版本号递增。

​ 由于旧数据并不真正的删除,所以必须对这些数据进行清理,innodb会开启一个后台线程执行清理工作,具体的规则是将删除版本号小于当前系统版本的行删除,这个过程叫做purge。

通过MVCC很好的实现了事务的隔离性,可以达到repeated read级别,要实现serializable还必须加锁。

参考:MVCC浅析

6 MyISAM和InnoDB

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

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

mysql 数据库引擎: http://www.cnblogs.com/0201zcr/p/5296843.html MySQL存储引擎--MyISAM与InnoDB区别: https://segmentfault.com/a/1190000008227211

网络

1 三次握手

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

2 四次挥手

注意: 中断连接端可以是客户端,也可以是服务器端. 下面仅以客户端断开连接举例, 反之亦然.

  1. 客户端发送一个数据分段, 其中的 FIN 标记设置为1. 客户端进入 FIN-WAIT 状态. 该状态下客户端只接收数据, 不再发送数据.
  2. 服务器接收到带有 FIN = 1 的数据分段, 发送带有 ACK = 1 的剩余数据分段, 确认收到客户端发来的 FIN 信息.
  3. 服务器等到所有数据传输结束, 向客户端发送一个带有 FIN = 1 的数据分段, 并进入 CLOSE-WAIT 状态, 等待客户端发来带有 ACK = 1 的确认报文.
  4. 客户端收到服务器发来带有 FIN = 1 的报文, 返回 ACK = 1 的报文确认, 为了防止服务器端未收到需要重发, 进入 TIME-WAIT 状态. 服务器接收到报文后关闭连接. 客户端等待 2MSL 后未收到回复, 则认为服务器成功关闭, 客户端关闭连接.

图解: http://blog.csdn.net/whuslei/article/details/6667471

3 ARP协议

地址解析协议(Address Resolution Protocol),其基本功能为透过目标设备的IP地址,查询目标的MAC地址,以保证通信的顺利进行。它是IPv4网络层必不可少的协议,不过在IPv6中已不再适用,并被邻居发现协议(NDP)所替代。

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

GET和POST有什么区别?及为什么网上的多数答案都是错的 知乎回答

get: RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1 post: RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1

6 Cookie和Session

 CookieSession
储存位置客户端服务器端
目的跟踪会话,也可以保存用户偏好设置或者保存用户名密码等跟踪会话
安全性不安全安全

session技术是要使用到cookie的,之所以出现session技术,主要是为了安全。

Cookie和Session都是为了保存客户端和服务端之间的交互状态,实现机制不同,各有优缺点。

  首先一个最大的区别就是Cookie是保存在客户端而Session就保存在服务端的。

  Cookie是客户端请求服务端时服务器会将一些信息以键值对的形式返回给客户端,保存在浏览器中,交互的时候可以加上这些Cookie值。用Cookie就可以方便的做一些缓存。Cookie的缺点是大小和数量都有限制;Cookie是存在客户端的可能被禁用、删除、篡改,是不安全的;Cookie如果很大,每次要请求都要带上,这样就影响了传输效率。

Session是基于Cookie来实现的,不同的是Session本身存在于服务端,但是每次传输的时候不会传输数据,只是把代表一个客户端的唯一ID(通常是JSESSIONID)写在客户端的Cookie中,这样每次传输这个ID就可以了。Session的优势就是传输数据量小,比较安全。

  但是Session也有缺点,就是如果Session不做特殊的处理容易失效、过期、丢失或者Session过多导致服务器内存溢出,并且要实现一个稳定可用安全的分布式Session框架也是有一定复杂度的。在实际使用中就要结合Cookie和Session的优缺点针对不同的问题来设计解决方案。

 

 

7 apache和nginx的区别

nginx 相对 apache 的优点:

  • 轻量级,同样起web 服务,比apache 占用更少的内存及资源
  • 抗并发,nginx 处理请求是异步非阻塞的,支持更多的并发连接,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能
  • 配置简洁
  • 高度模块化的设计,编写模块相对简单
  • 社区活跃

apache 相对nginx 的优点:

  • rewrite ,比nginx 的rewrite 强大
  • 模块超多,基本想到的都可以找到
  • 少bug ,nginx 的bug 相对较多
  • 超稳定

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

CGI是通用网关接口,是连接web服务器和应用程序的接口,用户通过CGI来获取动态数据或文件等。 CGI程序是一个独立的程序,它可以用几乎所有语言来写,包括perl,c,lua,python等等。

WSGI, Web Server Gateway Interface,是Python应用程序或框架和Web服务器之间的一种接口,WSGI的其中一个目的就是让用户可以用统一的语言(Python)编写前后端。

官方说明:PEP-3333

16 中间人攻击

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

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

17 c10k问题

所谓c10k问题,指的是服务器同时支持成千上万个客户端的问题,也就是concurrent 10 000 connection(这也是c10k这个名字的由来)。 推荐: https://my.oschina.net/xianggao/blog/664275

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

HTTP请求8种方法介绍 HTTP/1.1协议中共定义了8种HTTP请求方法,HTTP请求方法也被叫做“请求动作”,不同的方法规定了不同的操作指定的资源方式。服务端也会根据不同的请求方法做不同的响应。

GET

GET请求会显示请求指定的资源。一般来说GET方法应该只用于数据的读取,而不应当用于会产生副作用的非幂等的操作中。

GET会方法请求指定的页面信息,并返回响应主体,GET被认为是不安全的方法,因为GET方法会被网络蜘蛛等任意的访问。

HEAD

HEAD方法与GET方法一样,都是向服务器发出指定资源的请求。但是,服务器在响应HEAD请求时不会回传资源的内容部分,即:响应主体。这样,我们可以不传输全部内容的情况下,就可以获取服务器的响应头信息。HEAD方法常被用于客户端查看服务器的性能。

POST

POST请求会 向指定资源提交数据,请求服务器进行处理,如:表单数据提交、文件上传等,请求数据会被包含在请求体中。POST方法是非幂等的方法,因为这个请求可能会创建新的资源或/和修改现有资源。

PUT

PUT请求会身向指定资源位置上传其最新内容,PUT方法是幂等的方法。通过该方法客户端可以将指定资源的最新数据传送给服务器取代指定的资源的内容。

DELETE

DELETE请求用于请求服务器删除所请求URI(统一资源标识符,Uniform Resource Identifier)所标识的资源。DELETE请求后指定资源会被删除,DELETE方法也是幂等的。

CONNECT

CONNECT方法是HTTP/1.1协议预留的,能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接与非加密的HTTP代理服务器的通信。

OPTIONS

OPTIONS请求与HEAD类似,一般也是用于客户端查看服务器的性能。 这个方法会请求服务器返回该资源所支持的所有HTTP请求方法,该方法会用’*’来代替资源名称,向服务器发送OPTIONS请求,可以测试服务器功能是否正常。JavaScript的XMLHttpRequest对象进行CORS跨域资源共享时,就是使用OPTIONS方法发送嗅探请求,以判断是否有对指定资源的访问权限。 允许

TRACE

TRACE请求服务器回显其收到的请求信息,该方法主要用于HTTP请求的测试或诊断。

HTTP/1.1之后增加的方法

在HTTP/1.1标准制定之后,又陆续扩展了一些方法。其中使用中较多的是 PATCH 方法:

PATCH

PATCH方法出现的较晚,它在2010年的RFC 5789标准中被定义。PATCH请求与PUT请求类似,同样用于资源的更新。二者有以下两点不同:

但PATCH一般用于资源的部分更新,而PUT一般用于资源的整体更新。 当资源不存在时,PATCH会创建一个新的资源,而PUT只会对已在资源进行更新。

21 Ajax

AJAX,Asynchronous JavaScript and XML(异步的 JavaScript 和 XML), 是与在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术。

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

红黑树详解: https://xieguanglei.github.io/blog/post/red-black-tree.html

教你透彻了解红黑树: https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/03.01.md

编程题

1 台阶问题/斐波那契

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

fib = lambda n: n 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 for _ in xrange(n): a, b = b, a + b return b

2 变态台阶问题

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

fib = lambda n: n 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列二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

使用Step-wise线性搜索。

def get_value(l, r, c): return l[r][c] def find(l, x): m = len(l) - 1 n = len(l[0]) - 1 r = 0 c = n while c >= 0 and r <= m: value = get_value(l, r, c) if value == x: return True elif value > x: c = c - 1 elif value < x: r = r + 1 return False

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]

sorted排序并且用列表推导式.

l = ['b','c','d','b','c','a','a'] [single.append(i) for i in sorted(l) if i not in single] print single

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

pop弹出

a = [1,2,3,7] b = [3,4,5] def merge_sortedlist(a,b): c = [] while a and b: if a[0] >= b[0]: c.append(b.pop(0)) else: c.append(a.pop(0)) while a: c.append(a.pop(0)) while b: c.append(b.pop(0)) return c print merge_sortedlist(a,b)

9 交叉链表求交点

其实思想可以按照从尾开始比较两个链表,如果相交,则从尾开始必然一致,只要从尾开始比较,直至不一致的地方即为交叉点,如图所示

 

# 使用a,b两个list来模拟链表,可以看出交叉点是 7这个节点 a = [1,2,3,7,9,1,5] b = [4,5,7,9,1,5] for i in range(1,min(len(a),len(b))): if i==1 and (a[-1] != b[-1]): print "No" break else: if a[-i] != b[-i]: print "交叉节点:",a[-i+1] break else: pass

另外一种比较正规的方法,构造链表类

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

修改了一下:

#coding:utf-8 class ListNode: def __init__(self, x): self.val = x self.next = None def node(l1, l2): length1, length2 = 0, 0 # 求两个链表长度 while l1.next: l1 = l1.next#尾节点 length1 += 1 while l2.next: l2 = l2.next#尾节点 length2 += 1 #如果相交 if l1.next == l2.next: # 长的链表先走 if length1 > length2: for _ in range(length1 - length2): l1 = l1.next return l1#返回交点 else: for _ in range(length2 - length1): l2 = l2.next return l2#返回交点 # 如果不相交 else: return

思路: http://humaoli.blog.163.com/blog/static/13346651820141125102125995/

10 二分查找

#coding:utf-8 def binary_search(list,item): low = 0 high = len(list)-1 while low<=high: mid = (low+high)/2 guess = list[mid] if guess>item: high = mid-1 elif guess<item: low = mid+1 else: return mid return None mylist = [1,3,5,7,9] print binary_search(mylist,3)

参考: http://blog.csdn.net/u013205877/article/details/76411718

11 快排

#coding:utf-8 def quicksort(list): if len(list)<2: return list else: midpivot = list[0] lessbeforemidpivot = [i for i in list[1:] if i<=midpivot] biggerafterpivot = [i for i in list[1:] if i > midpivot] finallylist = quicksort(lessbeforemidpivot)+[midpivot]+quicksort(biggerafterpivot) return finallylist print quicksort([2,4,6,7,1,2,5])

更多排序问题可见:数据结构与算法-排序篇-Python描述

12 找零问题

#coding:utf-8 #values是硬币的面值values = [ 25, 21, 10, 5, 1] #valuesCounts 钱币对应的种类数 #money 找出来的总钱数 #coinsUsed 对应于目前钱币总数i所使用的硬币数目 def coinChange(values,valuesCounts,money,coinsUsed): #遍历出从1到money所有的钱数可能 for cents in range(1,money+1): minCoins = cents #把所有的硬币面值遍历出来和钱数做对比 for kind in range(0,valuesCounts): if (values[kind] <= cents): temp = coinsUsed[cents - values[kind]] +1 if (temp < minCoins): minCoins = temp coinsUsed[cents] = minCoins print ('面值:{0}的最少硬币使用数为:{1}'.format(cents, coinsUsed[cents]))

思路: http://blog.csdn.net/wdxin1322/article/details/9501163

方法: http://www.cnblogs.com/ChenxofHit/archive/2011/03/18/1988431.html

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): row = [root] while row:        print(row)        row = [kid for item in row for kid in (item.left, item.right) if kid]

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了

#coding:utf-8 #二叉树的遍历 #简单的二叉树节点类 class Node(object): def __init__(self,value,left,right): self.value = value self.left = left self.right = right #中序遍历:遍历左子树,访问当前节点,遍历右子树 def mid_travelsal(root): if root.left is None: mid_travelsal(root.left) #访问当前节点 print(root.value) if root.right is not None: mid_travelsal(root.right) #前序遍历:访问当前节点,遍历左子树,遍历右子树 def pre_travelsal(root): print (root.value) if root.left is not None: pre_travelsal(root.left) if root.right is not None: pre_travelsal(root.right) #后续遍历:遍历左子树,遍历右子树,访问当前节点 def post_trvelsal(root): if root.left is not None: post_trvelsal(root.left) if root.right is not None: post_trvelsal(root.right) print (root.value)

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

思路: http://blog.csdn.net/feliciafay/article/details/6841115

方法: http://www.xuebuyuan.com/2066385.html?mobile=1

22 两个字符串是否是变位词

class Anagram: """ @:param s1: The first string @:param s2: The second string @:return true or false """ def Solution1(s1,s2): alist = list(s2) pos1 = 0 stillOK = True while pos1 < len(s1) and stillOK: pos2 = 0 found = False while pos2 < len(alist) and not found: if s1[pos1] == alist[pos2]: found = True else: pos2 = pos2 + 1 if found: alist[pos2] = None else: stillOK = False pos1 = pos1 + 1 return stillOK print(Solution1('abcd','dcba')) def Solution2(s1,s2): alist1 = list(s1) alist2 = list(s2) alist1.sort() alist2.sort() pos = 0 matches = True while pos < len(s1) and matches: if alist1[pos] == alist2[pos]: pos = pos + 1 else: matches = False return matches print(Solution2('abcde','edcbg')) def Solution3(s1,s2): c1 = [0]*26 c2 = [0]*26 for i in range(len(s1)): pos = ord(s1[i])-ord('a') c1[pos] = c1[pos] + 1 for i in range(len(s2)): pos = ord(s2[i])-ord('a') c2[pos] = c2[pos] + 1 j = 0 stillOK = True while j<26 and stillOK: if c1[j] == c2[j]: j = j + 1 else: stillOK = False return stillOK print(Solution3('apple','pleap'))

23 动态规划问题

可参考:动态规划(DP)的整理-Python描述

 

24.PYTHON WEB框架

1. Django是一个开放源代码的Web应用框架,由Python写成。自带ORM。采用了MTV的框架模式,即模型M,模板T和视图V。

 

 

 

2. Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。

如此 Falsk 自由、灵活、可扩展性很强。很适用于开发小型应用、开发大型网站也没有压力,自己设计架构、选择合适第三方扩展,各方面性能完全不输Django。

 

常用拓展包:wtf、sqlalchemy、login、script、migrate、mail

 

3、Tornado

传说中实现高并发、高性能的框架。Tornado的全称是Torado Web Server,可用作Web服务器,同时也是一个Python Web的开发框架。

Tornado两大核心模块:

iostream :对非阻塞式的 socket 的简单封装,用来处理 socket 的异步读写

ioloop :核心的 I/O 循环。基于 epoll,可以高效的响应网络事

 

 

4.Bottle

Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python。它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系。

选择源码分析的版本是 Release 于 2009 年 7 月 11 日的 0.4.10 (这是我能找到的最早的发布版本了)。

为什么要分析 Bottle 这个比较冷门的框架?

  • Bottle 从发布至今一直贯彻的微型 Web 框架的理念。
  • Bottle 一直坚持单文件发布,也就是只有一个 bottle.py 文件。
  • 除了 Python 标准库之外没有依赖关系。
  • 与 Flask、Django 都遵循 PEP-3333 的 WSGI 协议。

 

所以,抛开框架的高级功能,单单从一个 Web 框架怎么处理请求的角度来看,Bottle 是最佳的选择。

Flask 从第一版开始就是依赖于 werkzeug 实现,更多的实现细节需要从 werkzeug 中查找。

Django 是个重型框架,不适合整体代码阅读,各个组件看看就可以。

Tornado 是个异类,和 WSGI 没有什么关系。

 

TCP三次握手与四次断开原理:

三次握手原理:

TCP是面向连接,无论哪方向另一方发送数据前,都必须先在双方间建立的一条连接,TCP协议提供可靠的连接服务。

 

第一次握手:建立连接,客户端发送连接请求报文段,将SYN位置为1,seq为x,然后客户端进入SYN_SEND状态,等待服务器确认

第二次握手:服务器收到SYN报文段,对该SYN进行确认,设置ack为x+1;同时也发送一个SYN请求信息,SYN位置为1,seq为y;服务端将上述信息放到报文段(即SYN+ACK)中,一并发给客户端,进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK报文段,将后将ack设置为y+1,向服务器发送ACK报文段,这个报文段发送完后,客户端与服务器都进入ESTABLISHED状态,完成TCP三次握手

 

 

四次挥手原理的几个状态要注意:

第一次挥手,A向B发送报FIN报文段,此时,A进入FIN_WAIT_1状态;

第二次挥手,主机B收到A发送的FIN报文,向A回一个ACK报文,主机A进入FIN_WAIT_2状态;

第三次挥手,主机B向主机A发送FIN报文,请求关闭连接,同时主机B进入LAST_ACK状态;

第四次挥手,A收到B的FIN报文,向B发ACK报文,然后A进入TIME_WAIT状态,主机B收到A的ACK后,就关闭连接;此时主机A等2msl后没收到回复,则证明对端已正常关闭,A也可以关闭

 

进程间的八种通信方式----共享内存是最快的 IPC 方式

1.无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

2.高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。

3.有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

4.消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5.信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6.信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

7.共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

8.套接字( socket ) : 套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

 

 

转载于:https://my.oschina.net/u/1412563/blog/2252037

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值