轉--Python語言特性

1 Python的函數參數傳遞

看兩個例子:html

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

 

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

 

全部的變量均可以理解是內存中一個對象的「引用」,或者,也能夠看似c中void*的感受。前端

這裏記住的是類型是屬於對象的,而不是變量。而對象有兩種,「可更改」(mutable)與「不可更改」(immutable)對象。java

在python中,strings, tuples, 和numbers是不可更改的對象,而list,dict等則是能夠修改的對象。(這就是這個問題的重點)node

當一個引用傳遞給函數的時候,函數自動複製一份引用,這個函數裏的引用和外邊的引用沒有半毛關係了.python

因此第一個例子裏函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用沒半毛感受.而第二個例子就不同了,函數內的引用指向的是可變對象,對它的操做就和定位了指針地址同樣,在內存裏進行修改.mysql

若是還不明白的話,這裏有更好的解釋: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-referencelinux

 

2 Python中的元類(metaclass)

這個很是的不經常使用,可是像ORM這種複雜的結構仍是會須要的,詳情請看:《深入理解Python中的元類(metaclass)nginx

 

3 @staticmethod和@classmethod

Python其實有3個方法,即靜態方法(staticmethod),類方法(classmethod)和實例方法,以下:git

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問題能夠了解下(新式類是廣度優先,舊式類是深度優先),<Python核心編程>裏講的也不少.

 

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的區別

推薦:《Python 2.7.x 和 3.x 版本的重要區別

====================================

操做系統

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. 先來先服務(FCFS, First Come First Serve)
  2. 短做業優先(SJF, Shortest Job First)
  3. 最高優先權調度(Priority Scheduling)
  4. 時間片輪轉(RR, Round Robin)
  5. 多級反饋隊列調度(multilevel feedback queue scheduling)

實時調度算法:

  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. 頁的大小固定,由系統肯定,將邏輯地址劃分爲頁號和頁內地址是由機器硬件實現的.
  4. 而段的長度卻不固定,決定於用戶所編寫的程序,一般由編譯程序在對源程序進行編譯時根據信息的性質來劃分.
  5. 分頁的做業地址空間是一維的.
  6. 分段的地址空間是二維的.

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 三次握手

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

GET和POST有什麼區別?及爲何網上的多數答案都是錯的

get: RFC 2616 – Hypertext Transfer Protocol — HTTP/1.1 post: RFC 2616 – Hypertext Transfer Protocol — HTTP/1.1

6 Cookie和Session

  Cookie 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這個名字的由來)。 推薦: http://www.kegel.com/c10k.html

18 socket

推薦: http://www.cnblogs.com/bingyun84/archive/2009/10/16/1584387.html

Socket=Ip address+ TCP/UDP + port

19 瀏覽器緩存

推薦: http://web.jobbole.com/84367/

304 Not Modified

20 HTTP1.0和HTTP1.1

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

  1. 請求頭Host字段,一個服務器多個網站
  2. 長連接
  3. 文件斷點續傳
  4. 身份認證,狀態管理,Cache緩存

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。

=========================================

編程題

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列二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

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 low if l[low] == t else 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)
    print binarySearch(l, 444)

 

 

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

 

 

感謝總結:https://github.com/taizilongxu/interview_python

相關文章
相關標籤/搜索