python最全面試題(附全面Python教程)

轉發評論+私信“學習”即可領取Python視頻教程


python最全面試題(附全面Python教程)


轉發評論+私信“學習”即可領取Python視頻教程

Python語言特性

1 Python的函數參數傳遞

看兩個如下例子,分析運行結果:

代碼一:

a = 1

def fun(a):

<code>a = 2
/<code>

fun(a)

print(a) # 1

代碼二:

a = []

def fun(a):

a.append(1)

fun(a)

print(a) # [1]

所有的變量都可以理解是內存中一個對象的“引用”,或者,也可以看似c中void*的感覺。

這裡記住的是類型是屬於對象的,而不是變量。而對象有兩種,“可更改”(mutable)與“不可更改”(immutable)對象。在python中,strings, tuples, 和numbers是不可更改的對象,而list,dict等則是可以修改的對象。(這就是這個問題的重點)

當一個引用傳遞給函數的時候,函數自動複製一份引用,這個函數里的引用和外邊的引用沒有半毛關係了.所以第一個例子裡函數把引用指向了一個不可變對象,當函數返回的時候,外面的引用沒半毛感覺.而第二個例子就不一樣了,函數內的引用指向的是可變對象,對它的操作就和定位了指針地址一樣,在內存裡進行修改.

2 Python中的元類(metaclass)

元類就是用來創建類的“東西”。你創建類就是為了創建類的實例對象,但是我們已經學習到了Python中的類也是對象。好吧,元類就是用來創建這些類(對象)的,元類就是類的類

這個非常的不常用,詳情請看:《深刻理解Python中的元類(metaclass)》

3 @staticmethod和@classmethod

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

class A(object):

def foo(self,x):

print "executing foo(%s,%s)"%(self,x)

@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(self, x),為什麼要這麼做呢?因為實例方法的調用離不開實例,我們需要把實例自己傳給函數,調用的時候是這樣的a.foo(x)(其實是foo(a, x)).類方法一樣,只不過它傳遞的是類而不是實例,A.class_foo(x).注意這裡的self和cls可以替換別的參數,但是python的約定是這倆,還是不要改的好.

對於靜態方法其實和普通的方法一樣,不需要對誰進行綁定,唯一的區別是調用的時候需要使用a.static_foo(x)或者A.static_foo(x)來調用.

\\

實例方法

類方法

靜態方法

a = A()

a.foo(x)

a.class_foo(x)

a.static_foo(x)

A

不可用

A.class_foo(x)

A.static_foo(x)

4 類變量和實例變量

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]

5 Python自省

這個也是python彪悍的特性.

自省就是面向對象的語言所寫的程序在運行時,所能知道對象的類型.簡單一句就是運行時能夠獲得對象的類型.比如type(),dir(),getattr(),hasattr(),isinstance().

6 字典推導式

可能你見過列表推導時,卻沒有見過字典推導式,在2.7中才加入的:

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

7 Python中單下劃線和雙下劃線

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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

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://www.zhihu.com/question/19754941

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

.format在許多方面看起來更便利.對於%最煩人的是它無法同時傳遞一個變量和元組.你可能會想下面的代碼不會有什麼問題:

Python:

"hi there %s" % name

但是,如果name恰好是(1,2,3),它將會拋出一個TypeError異常.為了保證它總是正確的,你必須這樣做:

"hi there %s" % (name,) # 提供一個單元素的數組而不是一個參數

9 迭代器和生成器

在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。

可以被next()函數調用並不斷返回下一個值的對象稱為迭代器:Iterator。

這個是stackoverflow裡python排名第一的問題,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python

10argsand*kwargs

用args和*kwargs只是為了方便並沒有強制使用它們.

當你不確定你的函數里將要傳遞多少參數時你可以用*args.例如,它可以傳遞任意數量的參數:

1

2

3

4

5

6

7

8

def print_everything(*args):

for count, thing in enumerate(args):

... print '{0}. {1}'.format(count, thing)

...

print_everything('apple', 'banana', 'cabbage')

  1. apple
  2. banana
  3. cabbage

相似的,**kwargs允許你使用沒有事先定義的參數名:

1

2

3

4

5

6

7

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.命名參數在列表的最前端.例如:

1

def table_things(titlestring, **kwargs)

args和kwargs可以同時在函數的定義中,但是args必須在**kwargs前面.

當調用函數時你也可以用和*語法.例如:

1

2

3

4

5

6

7

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 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那麼不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。

那麼對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是缺省參數。對那些缺少的參數設定為缺省參數即可解決問題。因為你假設函數功能相同,那麼那些缺少的參數終歸是需要用的。

好了,鑑於情況 1 跟 情況 2 都有了解決方案,python 自然就不需要函數重載了。

14 新式類和舊式類

這個面試官問了,我說了老半天,不知道他問的真正意圖是什麼.

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

新式類很早在2.2就出現了,所以舊式類完全是兼容的問題,Python3裡的類全部都是新式類.這裡有一個MRO問題可以瞭解下(新式類是廣度優先,舊式類是深度優先),<python>裡講的也很多./<python>

15__new__和init的區別

這個new確實很少見到,先做了解吧.

new是一個靜態方法,而init是一個實例方法.

new方法會返回一個創建的實例,而init什麼都不返回.

只有在new返回一個cls的實例時後面的init才能被調用.

當創建一個新實例時調用new,初始化一個實例時用init.

ps:metaclass是創建類時起作用.所以我們可以分別使用metaclass,newinit來分別在類創建,實例創建和實例初始化的時候做一些小手腳.

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指向同一個字典,這樣它們具有相同的屬性和方法.

1

2

3

4

5

6

7

8

9

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 裝飾器版本

1

2

3

4

5

6

7

8

9

10

11

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為了保證線程安全而採取的獨立線程運行的限制,說白了就是一個核只能在同一時間運行一個線程.

解決辦法就是多進程和下面的協程(協程也只是單CPU,但是能減小切換代價提升性能).

19 協程

簡單點說協程是進程和線程的升級版,進程和線程都面臨著內核態和用戶態的切換問題而耗費許多切換時間,而協程就是用戶自己控制切換的時機,不再需要陷入系統的內核態.

Python裡最常見的yield就是協程的思想!可以查看第九個問題.

20 閉包

閉包(closure)是函數式編程的重要的語法結構。閉包也是一種組織代碼的結構,它同樣提高了代碼的可重複使用性。

當一個內嵌函數引用其外部作作用域的變量,我們就會得到一個閉包. 總結一下,創建一個閉包必須滿足以下幾點:

必須有一個內嵌函數

內嵌函數必須引用外部函數中的變量

外部函數的返回值必須是內嵌函數

感覺閉包還是有難度的,幾句話是說不明白的,還是查查相關資料.

重點是函數運行後並不會被撤銷,就像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()的區別

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

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時,該對象生命就結束了。

優點:

簡單

實時性

缺點:

維護引用計數消耗資源

循環引用

2 標記-清除機制

基本思路是先按需分配,等到沒有空閒內存的時候從寄存器和程序棧上的引用出發,遍歷以對象為節點、以引用為邊構成的圖,把所有可以訪問到的對象打上標記,然後清掃一遍內存空間,把所有沒標記的對象釋放。

3 分代技術

分代回收的整體思想是:將系統中的所有內存塊根據其存活時間劃分為不同的集合,每個集合就成為一個“代”,垃圾收集頻率隨著“代”的存活時間的增大而減小,存活時間通常利用經過幾次垃圾回收來度量。

Python默認定義了三代對象集合,索引數越大,對象存活時間越長。

舉例:

當某些內存塊M經過了3次垃圾收集的清洗之後還存活時,我們就將內存塊M劃到一個集合A中去,而新分配的內存都劃分到集合B中去。當垃圾收集開始工作時,大多數情況都只對集合B進行垃圾回收,而對集合A進行垃圾回收要隔相當長一段時間後才進行,這就使得垃圾收集機制需要處理的內存少了,效率自然就提高了。在這個過程中,集合B中的某些內存塊由於存活時間長而會被轉移到集合A中,當然,集合A中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而被延遲。

25 Python裡面如何實現tuple和list的轉換?

答:tuple,可以說是不可變的list,訪問方式還是通過索引下標的方式。

當你明確定義個tuple是,如果僅有一個元素,必須帶有,例如:(1,)。

當然,在2.7以後的版,python裡還增加了命名式的tuple!

至於有什麼用,首先第一點,樓主玩過python都知道,python的函數可以有多返回值的,而python裡,多返回值,就是用tuple來表示,這是用的最廣的了,

比如說,你需要定義一個常量的列表,但你又不想使用list,那也可以是要你管tuple,例如:

if a in ('A','B','C'):pass

26 Python的is

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

27 read,readline和readlines

read 讀取整個文件

readline 讀取下一行,使用生成器方法

readlines 讀取整個文件到一個迭代器以供我們遍歷

28 Python2和3的區別

大部分Python庫都同時支持Python 2.7.x和3.x版本的,所以不論選擇哪個版本都是可以的。但為了在使用Python時避開某些版本中一些常見的陷阱,或需要移植某個Python項目

使用future模塊

print函數

整數除法

Unicode

xrange

觸發異常

處理異常

next()函數和.next()方法

For循環變量與全局命名空間洩漏

比較無序類型

使用input()解析輸入內容

返回可迭代對象,而不是列表

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

29到底什麼是Python?你可以在回答中與其他技術進行對比

答案

下面是一些關鍵點:

Python是一種解釋型語言。這就是說,與C語言和C的衍生語言不同,Python代碼在運行之前不需要編譯。其他解釋型語言還包括PHP和Ruby。

Python是動態類型語言,指的是你在聲明變量時,不需要說明變量的類型。你可以直接編寫類似x=111和x="I'm a string"這樣的代碼,程序不會報錯。

Python非常適合面向對象的編程(OOP),因為它支持通過組合(composition)與繼承(inheritance)的方式定義類(class)。Python中沒有訪問說明符(access specifier,類似C++中的public和private),這麼設計的依據是“大家都是成年人了”。

在Python語言中,函數是第一類對象(first-class objects)。這指的是它們可以被指定給變量,函數既能返回函數類型,也可以接受函數作為輸入。類(class)也是第一類對象。

Python代碼編寫快,但是運行速度比編譯語言通常要慢。好在Python允許加入基於C語言編寫的擴展,因此我們能夠優化代碼,消除瓶頸,這點通常是可以實現的。numpy就是一個很好地例子,它的運行速度真的非常快,因為很多算術運算其實並不是通過Python實現的。

Python用途非常廣泛——網絡應用,自動化,科學建模,大數據應用,等等。它也常被用作“膠水語言”,幫助其他語言和組件改善運行狀況。

Python讓困難的事情變得容易,因此程序員可以專注於算法和數據結構的設計,而不用處理底層的細節。

為什麼提這個問題:

如果你應聘的是一個Python開發崗位,你就應該知道這是門什麼樣的語言,以及它為什麼這麼酷。以及它哪裡不好。

30補充缺失的代碼

def print_directory_contents(sPath):

<code>"""

這個函數接受文件夾的名稱作為輸入參數,

返回該文件夾中文件的路徑,


以及其包含文件夾中文件的路徑。

"""

# 補充代碼
/<code>

答案

def print_directory_contents(sPath):

<code>import os                                     

for sChild in os.listdir(sPath):

sChildPath = os.path.join(sPath,sChild)

if os.path.isdir(sChildPath):

print_directory_contents(sChildPath)

else:

print sChildPath
/<code>

特別要注意以下幾點:

命名規範要統一。如果樣本代碼中能夠看出命名規範,遵循其已有的規範。

遞歸函數需要遞歸併終止。確保你明白其中的原理,否則你將面臨無休無止的調用棧(callstack)。

我們使用os模塊與操作系統進行交互,同時做到交互方式是可以跨平臺的。你可以把代碼寫成sChildPath = sPath + '/' + sChild,但是這個在Windows系統上會出錯。

熟悉基礎模塊是非常有價值的,但是別想破腦袋都背下來,記住Google是你工作中的良師益友。

如果你不明白代碼的預期功能,就大膽提問。

堅持KISS原則!保持簡單,不過腦子就能懂!

為什麼提這個問題:

說明面試者對與操作系統交互的基礎知識

遞歸真是太好用啦

31閱讀下面的代碼,寫出A0,A1至An的最終值。

A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5)))

A1 = range(10)

A2 = [i for i in A1 if i in A0]

A3 = [A0[s] for s in A0]

A4 = [i for i in A1 if i in A3]

A5 = {i:i*i for i in A1}

A6 = [[i,i*i] for i in A1]

答案

A0 = {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}

A1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

A2 = []

A3 = [1, 3, 2, 5, 4]

A4 = [1, 2, 3, 4, 5]

A5 = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

A6 = [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]]

為什麼提這個問題:

列表解析(list comprehension)十分節約時間,對很多人來說也是一個大的學習障礙。

如果你讀懂了這些代碼,就很可能可以寫下正確地值。

其中部分代碼故意寫的怪怪的。因為你共事的人之中也會有怪人。

32下面代碼會輸出什麼:

def f(x,l=[]):

<code>for i in range(x):

l.append(i*i)

print(l)
/<code>

f(2)

f(3,[3,2,1])

f(3)

答案:

[0, 1]

[3, 2, 1, 0, 1, 4]

[0, 1, 0, 1, 4]

呃?

第一個函數調用十分明顯,for循環先後將0和1添加至了空列表l中。l是變量的名字,指向內存中存儲的一個列表。第二個函數調用在一塊新的內存中創建了新的列表。l這時指向了新生成的列表。之後再往新列表中添加0、1和4。很棒吧。第三個函數調用的結果就有些奇怪了。它使用了之前內存地址中存儲的舊列表。這就是為什麼它的前兩個元素是0和1了。

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

答案:

版本管理!被問到這個問題的時候,你應該要表現得很興奮,甚至告訴他們你是如何使用Git(或是其他你最喜歡的工具)追蹤自己和奶奶的書信往來。我偏向於使用Git作為版本控制系統(VCS),但還有其他的選擇,比如subversion(SVN)。

為什麼提這個問題:

因為沒有版本控制的代碼,就像沒有杯子的咖啡。有時候我們需要寫一些一次性的、可以隨手扔掉的腳本,這種情況下不作版本控制沒關係。但是如果你面對的是大量的代碼,使用版本控制系統是有利的。版本控制能夠幫你追蹤誰對代碼庫做了什麼操作;發現新引入了什麼bug;管理你的軟件的不同版本和發行版;在團隊成員中分享源代碼;部署及其他自動化處理。它能讓你回滾到出現問題之前的版本,單憑這點就特別棒了。還有其他的好功能。怎麼一個棒字了得!

34“猴子補丁”(monkey patching)指的是什麼?這種做法好嗎?

答案:

“猴子補丁”就是指,在函數或對象已經定義之後,再去改變它們的行為。

舉個例子:

import datetime

datetime.datetime.now = lambda: datetime.datetime(2012, 12, 12)

大部分情況下,這是種很不好的做法 - 因為函數在代碼庫中的行為最好是都保持一致。打“猴子補丁”的原因可能是為了測試。mock包對實現這個目的很有幫助。

為什麼提這個問題?

答對這個問題說明你對單元測試的方法有一定了解。你如果提到要避免“猴子補丁”,可以說明你不是那種喜歡花裡胡哨代碼的程序員(公司裡就有這種人,跟他們共事真是糟糕透了),而是更注重可維護性。還記得KISS原則碼?答對這個問題還說明你明白一些Python底層運作的方式,函數實際是如何存儲、調用等等。

另外:如果你沒讀過mock模塊的話,真的值得花時間讀一讀。這個模塊非常有用。

35閱讀下面的代碼,它的輸出結果是什麼?

class A(object):

<code>def go(self):

print "go A go!"

def stop(self):

print "stop A stop!"

def pause(self):

raise Exception("Not Implemented")
/<code>

class B(A):

<code>def go(self):

super(B, self).go()

print "go B go!"
/<code>

class C(A):

<code>def go(self):

super(C, self).go()

print "go C go!"

def stop(self):

super(C, self).stop()

print "stop C stop!"
/<code>

class D(B,C):

<code>def go(self):

super(D, self).go()

print "go D go!"


def stop(self):

super(D, self).stop()

print "stop D stop!"

def pause(self):

print "wait D wait!"
/<code>

class E(B,C): pass

a = A()

b = B()

c = C()

d = D()

e = E()

說明下列代碼的輸出結果

a.go()

b.go()

c.go()

d.go()

e.go()

a.stop()

b.stop()

c.stop()

d.stop()

e.stop()

a.pause()

b.pause()

c.pause()

d.pause()

e.pause()

答案

輸出結果以註釋的形式表示:

a.go()

go A go!

b.go()

go A go!

go B go!

c.go()

go A go!

go C go!

d.go()

go A go!

go C go!

go B go!

go D go!

e.go()

go A go!

go C go!

go B go!

a.stop()

stop A stop!

b.stop()

stop A stop!

c.stop()

stop A stop!

stop C stop!

d.stop()

stop A stop!

stop C stop!

stop D stop!

e.stop()

stop A stop!

a.pause()

... Exception: Not Implemented

b.pause()

... Exception: Not Implemented

c.pause()

... Exception: Not Implemented

d.pause()

wait D wait!

e.pause()

...Exception: Not Implemented

為什麼提這個問題?

因為面向對象的編程真的真的很重要。不騙你。答對這道問題說明你理解了繼承和Python中super函數的用法。

36閱讀下面的代碼,它的輸出結果是什麼?

class Node(object):

<code>def __init__(self,sName):

self._lChildren = []

self.sName = sName

def __repr__(self):

return "<node>".format(self.sName)

def append(self,*args,**kwargs):

self._lChildren.append(*args,**kwargs)

def print_all_1(self):

print self

for oChild in self._lChildren:

oChild.print_all_1()

def print_all_2(self):

def gen(o):

lAll = [o,]

while lAll:

oNext = lAll.pop(0)

lAll.extend(oNext._lChildren)

yield oNext

for oNode in gen(self):

print oNode
/<node>/<code>

oRoot = Node("root")

oChild1 = Node("child1")

oChild2 = Node("child2")

oChild3 = Node("child3")

oChild4 = Node("child4")

oChild5 = Node("child5")

oChild6 = Node("child6")

oChild7 = Node("child7")

oChild8 = Node("child8")

oChild9 = Node("child9")

oChild10 = Node("child10")

oRoot.append(oChild1)

oRoot.append(oChild2)

oRoot.append(oChild3)

oChild1.append(oChild4)

oChild1.append(oChild5)

oChild2.append(oChild6)

oChild4.append(oChild7)

oChild3.append(oChild8)

oChild3.append(oChild9)

oChild6.append(oChild10)

說明下面代碼的輸出結果

oRoot.print_all_1()

oRoot.print_all_2()

答案

oRoot.print_all_1()會打印下面的結果:

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

oRoot.print_all_1()會打印下面的結果:

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

<node>

為什麼提這個問題?

因為對象的精髓就在於組合(composition)與對象構造(object construction)。對象需要有組合成分構成,而且得以某種方式初始化。這裡也涉及到遞歸和生成器(generator)的使用。

生成器是很棒的數據類型。你可以只通過構造一個很長的列表,然後打印列表的內容,就可以取得與print_all_2類似的功能。生成器還有一個好處,就是不用佔據很多內存。

有一點還值得指出,就是print_all_1會以深度優先(depth-first)的方式遍歷樹(tree),而print_all_2則是寬度優先(width-first)。有時候,一種遍歷方式比另一種更合適。但這要看你的應用的具體情況。

36.介紹一下except的用法和作用?

答:try…except…except…[else…][finally…]

執行try下的語句,如果引發異常,則執行過程會跳到except語句。對每個except分支順序嘗試執行,如果引發的異常與except中的異常組匹配,執行相應的語句。如果所有的except都不匹配,則異常會傳遞到下一個調用本代碼的最高層try代碼中。

try下的語句正常執行,則執行else塊代碼。如果發生異常,就不會執行

如果存在finally語句,最後總是會執行。

37.Python中pass語句的作用是什麼?

答:pass語句不會執行任何操作,一般作為佔位符或者創建佔位程序,whileFalse:pass

38.介紹一下Python下range()函數的用法?

答:列出一組數據,經常用在for in range()循環中

39.如何用Python來進行查詢和替換一個文本字符串?

答:可以使用re模塊中的sub()函數或者subn()函數來進行查詢和替換,

格式:sub(replacement, string[,count=0])(replacement是被替換成的文本,string是需要被替換的文本,count是一個可選參數,指最大被替換的數量)

import re

p=re.compile(‘blue|white|red’)

print(p.sub(‘colour’,'blue socks and red shoes’))

colour socks and colourshoes

print(p.sub(‘colour’,'blue socks and red shoes’,count=1))

colour socks and redshoes

subn()方法執行的效果跟sub()一樣,不過它會返回一個二維數組,包括替換後的新的字符串和總共替換的數量

40.Python裡面match()和search()的區別?

答:re模塊中match(pattern,string[,flags]),檢查string的開頭是否與pattern匹配。

re模塊中research(pattern,string[,flags]),在string搜索pattern的第一個匹配值。

print(re.match(‘super’, ‘superstition’).span())

(0, 5)

print(re.match(‘super’, ‘insuperable’))

None

print(re.search(‘super’, ‘superstition’).span())

(0, 5)

print(re.search(‘super’, ‘insuperable’).span())

(2, 7)

41.用Python匹配HTML tag的時候,<.>和<.>有什麼區別?

答:術語叫貪婪匹配( <.> )和非貪婪匹配(<.> )

例如:

test

<.> :

test

<.> :

42.Python裡面如何生成隨機數?

答:random模塊

隨機整數:random.randint(a,b):返回隨機整數x,a<=x<=b

random.randrange(start,stop,[,step]):返回一個範圍在(start,stop,step)之間的隨機整數,不包括結束值。

隨機實數:random.random( ):返回0到1之間的浮點數

random.uniform(a,b):返回指定範圍內的浮點數。

43.有沒有一個工具可以幫助查找python的bug和進行靜態的代碼分析?

答:PyChecker是一個python代碼的靜態分析工具,它可以幫助查找python代碼的bug, 會對代碼的複雜度和格式提出警告

Pylint是另外一個工具可以進行codingstandard檢查

44.如何在一個function裡面設置一個全局的變量?

答:解決方法是在function的開始插入一個global聲明:

def f()

global x

45.單引號,雙引號,三引號的區別

答:單引號和雙引號是等效的,如果要換行,需要符號(),三引號則可以直接換行,並且可以包含註釋

如果要表示Let’s go 這個字符串

單引號:s4 = ‘Let\\’s go’

雙引號:s5 = “Let’s go”

s6 = ‘I realy like“python”!’

這就是單引號和雙引號都可以表示字符串的原因了

46 Python和多線程(multi-threading)。這是個好主意碼?列舉一些讓Python代碼以並行方式運行的方法。

答案

Python並不支持真正意義上的多線程。Python中提供了多線程包,但是如果你想通過多線程提高代碼的速度,使用多線程包並不是個好主意。Python中有一個被稱為Global Interpreter Lock(GIL)的東西,它會確保任何時候你的多個線程中,只有一個被執行。線程的執行速度非常之快,會讓你誤以為線程是並行執行的,但是實際上都是輪流執行。經過GIL這一道關卡處理,會增加執行的開銷。這意味著,如果你想提高代碼的運行速度,使用threading包並不是一個很好的方法。

不過還是有很多理由促使我們使用threading包的。如果你想同時執行一些任務,而且不考慮效率問題,那麼使用這個包是完全沒問題的,而且也很方便。但是大部分情況下,並不是這麼一回事,你會希望把多線程的部分外包給操作系統完成(通過開啟多個進程),或者是某些調用你的Python代碼的外部程序(例如Spark或Hadoop),又或者是你的Python代碼調用的其他代碼(例如,你可以在Python中調用C函數,用於處理開銷較大的多線程工作)。

為什麼提這個問題

因為GIL就是個混賬東西(A-hole)。很多人花費大量的時間,試圖尋找自己多線程代碼中的瓶頸,直到他們明白GIL的存在。

47 將下面的函數按照執行效率高低排序。

它們都接受由0至1之間的數字構成的列表作為輸入。這個列表可以很長。一個輸入列表的示例如下:[random.random() for i in range(100000)]。你如何證明自己的答案是正確的。

def f1(lIn):

<code>l1 = sorted(lIn)

l2 = [i for i in l1 if i<0.5]

return [i*i for i in l2]
/<code>

def f2(lIn):

<code>l1 = [i for i in lIn if i<0.5]

l2 = sorted(l1)

return [i*i for i in l2]
/<code>

def f3(lIn):

<code>l1 = [i*i for i in lIn]

l2 = sorted(l1)

return [i for i in l1 if i/<code>

答案

按執行效率從高到低排列:f2、f1和f3。要證明這個答案是對的,你應該知道如何分析自己代碼的性能。Python中有一個很好的程序分析包,可以滿足這個需求。

import cProfile

lIn = [random.random() for i in range(100000)]

cProfile.run('f1(lIn)')

cProfile.run('f2(lIn)')

cProfile.run('f3(lIn)')

為了向大家進行完整地說明,下面我們給出上述分析代碼的輸出結果:

cProfile.run('f1(lIn)')

<code>    4 function calls in 0.045 seconds
/<code>

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

<code>    1    0.009    0.009    0.044    0.044 <stdin>:1(f1)

1 0.001 0.001 0.045 0.045 <string>:1(<module>)

1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}

1 0.035 0.035 0.035 0.035 {sorted}
/<module>/<string>/<stdin>/<code>

cProfile.run('f2(lIn)')

<code>    4 function calls in 0.024 seconds
/<code>

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

<code>    1    0.008    0.008    0.023    0.023 <stdin>:1(f2)

1 0.001 0.001 0.024 0.024 <string>:1(<module>)

1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}

1 0.016 0.016 0.016 0.016 {sorted}
/<module>/<string>/<stdin>/<code>

cProfile.run('f3(lIn)')

<code>    4 function calls in 0.055 seconds
/<code>

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

<code>    1    0.016    0.016    0.054    0.054 <stdin>:1(f3)

1 0.001 0.001 0.055 0.055 <string>:1(<module>)

1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}

1 0.038 0.038 0.038 0.038 {sorted}
/<module>/<string>/<stdin>/<code>

為什麼提這個問題?

定位並避免代碼瓶頸是非常有價值的技能。想要編寫許多高效的代碼,最終都要回答常識上來——在上面的例子中,如果列表較小的話,很明顯是先進行排序更快,因此如果你可以在排序前先進行篩選,那通常都是比較好的做法。其他不顯而易見的問題仍然可以通過恰當的工具來定位。因此瞭解這些工具是有好處的。

48.如何用Python來進行查詢和替換一個文本字符串?

可以使用sub()方法來進行查詢和替換,sub方法的格式為:sub(replacement, string[, count=0])

replacement是被替換成的文本

string是需要被替換的文本

count是一個可選參數,指最大被替換的數量

49.Python裡面search()和match()的區別?

match()函數只檢測RE是不是在string的開始位置匹配,search()會掃描整個string查找匹配, 也就是說match()只有在0位置匹配成功的話才有返回,如果不是開始位置匹配成功的話,match()就返回none

50.用Python匹配HTML tag的時候,<.>和<.>有什麼區別?

前者是貪婪匹配,會從頭到尾匹配 ,而後者是非貪婪匹配,只匹配到第一個 >。

51.Python裡面如何生成隨機數?

import random

random.random()

它會返回一個隨機的0和1之間的浮點數

操作系統

1 select,poll和epoll

其實所有的I/O都是輪詢的方法,只不過實現的層面不同罷了.

這個問題可能有點深入了,但相信能回答出這個問題是對I/O多路複用有很好的瞭解了.其中tornado使用的就是epoll的.

基本上select有3個缺點:

連接數受限

查找配對速度慢

數據由內核拷貝到用戶態

poll改善了第一個缺點

epoll改了三個缺點.

2 調度算法

先來先服務(FCFS, First Come First Serve)

短作業優先(SJF, Shortest Job First)

最高優先權調度(Priority Scheduling)

時間片輪轉(RR, Round Robin)

多級反饋隊列調度(multilevel feedback queue scheduling)

實時調度算法:

最早截至時間優先 EDF

最低鬆弛度優先 LLF

3 死鎖

原因:

競爭資源

程序推進順序不當

必要條件:

互斥條件

請求和保持條件

不剝奪條件

環路等待條件

處理死鎖基本方法:

預防死鎖(摒棄除1以外的條件)

避免死鎖(銀行家算法)

檢測死鎖(資源分配圖)

解除死鎖

剝奪資源

撤銷進程

4 程序編譯與鏈接

Bulid過程可以分解為4個步驟:預處理(Prepressing), 編譯(Compilation)、彙編(Assembly)、鏈接(Linking)

以c語言為例:

1 預處理

預編譯過程主要處理那些源文件中的以“#”開始的預編譯指令,主要處理規則有:

將所有的“#define”刪除,並展開所用的宏定義

處理所有條件預編譯指令,比如“#if”、“#ifdef”、 “#elif”、“#endif”

處理“#include”預編譯指令,將被包含的文件插入到該編譯指令的位置,注:此過程是遞歸進行的

刪除所有註釋

添加行號和文件名標識,以便於編譯時編譯器產生調試用的行號信息以及用於編譯時產生編譯錯誤或警告時可顯示行號

保留所有的#pragma編譯器指令。

2 編譯

編譯過程就是把預處理完的文件進行一系列的詞法分析、語法分析、語義分析及優化後生成相應的彙編代碼文件。這個過程是整個程序構建的核心部分。

3 彙編

彙編器是將彙編代碼轉化成機器可以執行的指令,每一條彙編語句幾乎都是一條機器指令。經過編譯、鏈接、彙編輸出的文件成為目標文件(Object File)

4 鏈接

鏈接的主要內容就是把各個模塊之間相互引用的部分處理好,使各個模塊可以正確的拼接。

鏈接的主要過程包塊 地址和空間的分配(Address and Storage Allocation)、符號決議(Symbol Resolution)和重定位(Relocation)等步驟。

5 靜態鏈接和動態鏈接

靜態鏈接方法:靜態鏈接的時候,載入代碼就會把程序會用到的動態代碼或動態代碼的地址確定下來

靜態庫的鏈接可以使用靜態鏈接,動態鏈接庫也可以使用這種方法鏈接導入庫

動態鏈接方法:使用這種方式的程序並不在一開始就完成動態鏈接,而是直到真正調用動態庫代碼時,載入程序才計算(被調用的那部分)動態代碼的邏輯地址,然後等到某個時候,程序又需要調用另外某塊動態代碼時,載入程序又去計算這部分代碼的邏輯地址,所以,這種方式使程序初始化時間較短,但運行期間的性能比不上靜態鏈接的程序

6 虛擬內存技術

虛擬存儲器是值具有請求調入功能和置換功能,能從邏輯上對內存容量加以擴充的一種存儲系統.

7 分頁和分段

分頁: 用戶程序的地址空間被劃分成若干固定大小的區域,稱為“頁”,相應地,內存空間分成若干個物理塊,頁和塊的大小相等。可將用戶程序的任一頁放在內存的任一塊中,實現了離散分配。

分段: 將用戶程序地址空間分成若干個大小不等的段,每段可以定義一組相對完整的邏輯信息。存儲分配時,以段為單位,段與段在內存中可以不相鄰接,也實現了離散分配。

分頁與分段的主要區別

頁是信息的物理單位,分頁是為了實現非連續分配,以便解決內存碎片問題,或者說分頁是由於系統管理的需要.段是信息的邏輯單位,它含有一組意義相對完整的信息,分段的目的是為了更好地實現共享,滿足用戶的需要.

頁的大小固定,由系統確定,將邏輯地址劃分為頁號和頁內地址是由機器硬件實現的.而段的長度卻不固定,決定於用戶所編寫的程序,通常由編譯程序在對源程序進行編譯時根據信息的性質來劃分.

分頁的作業地址空間是一維的.分段的地址空間是二維的.

8 頁面置換算法

最佳置換算法OPT:不可能實現

先進先出FIFO

最近最久未使用算法LRU:最近一段時間裡最久沒有使用過的頁面予以置換.

clock算法

9 邊沿觸發和水平觸發

邊緣觸發是指每當狀態變化時發生一個 io 事件,條件觸發是隻要滿足條件就發生一個 io 事件

數據庫

1 事務

數據庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。

2 數據庫索引

索引是對數據庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問數據庫表中的特定信息。

索引分為聚簇索引和非聚簇索引兩種,聚簇索引 是按照數據存放的物理位置為順序的,而非聚簇索引就不一樣了;聚簇索引能提高多行檢索的速度,而非聚簇索引對於單行的檢索很快。

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

3 Redis原理

4 樂觀鎖和悲觀鎖

悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操作

樂觀鎖:假設不會發生併發衝突,只在提交操作時檢查是否違反數據完整性。

5 MVCC

大多數的MySQL事務型存儲引擎,如InnoDB,Falcon以及PBXT都不使用一種簡單的行鎖機制。事實上,他們都和另外一種用來增加併發性的被稱為“多版本併發控制(MVCC)”的機制來一起使用。MVCC不只使用在MySQL中,Oracle、PostgreSQL,以及其他一些數據庫系統也同樣使用它。

6 MyISAM和InnoDB

MyISAM 適合於一些需要大量查詢的應用,但其對於有大量寫操作並不是很好。甚至你只是需要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都無法操作直到讀操作完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。

InnoDB 的趨勢會是一個非常複雜的存儲引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支持“行鎖” ,於是在寫操作比較多的時候,會更優秀。並且,他還支持更多的高級應用,比如:事務。

網絡

1 三次握手

客戶端通過向服務器端發送一個SYN來創建一個主動打開,作為三路握手的一部分。客戶端把這段連接的序號設定為隨機數 A。

服務器端應當為一個合法的SYN回送一個SYN/ACK。ACK 的確認碼應為 A+1,SYN/ACK 包本身又有一個隨機序號 B。

最後,客戶端再發送一個ACK。當服務端受到這個ACK的時候,就完成了三路握手,並進入了連接創建狀態。此時包序號被設定為收到的確認號 A+1,而響應則為 B+1。

2 四次揮手

CP的連接的拆除需要發送四個包,因此稱為四次揮手(four-way handshake)。客戶端或服務器均可主動發起揮手動作,在socket編程中,任何一方執行close()操作即可產生揮手操作。

(1)客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送。

(2)服務器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1。和SYN一樣,一個FIN將佔用一個序號。

(3)服務器B關閉與客戶端A的連接,發送一個FIN給客戶端A。

(4)客戶端A發回ACK報文確認,並將確認序號設置為收到序號加1。

3 ARP協議

地址解析協議(Address Resolution Protocol): 根據IP地址獲取物理地址的一個TCP/IP協議

4 urllib和urllib2的區別

這個面試官確實問過,當時答的urllib2可以Post而urllib不可以.

urllib提供urlencode方法用來GET查詢字符串的產生,而urllib2沒有。這是為何urllib常和urllib2一起使用的原因。

urllib2可以接受一個Request類的實例來設置URL請求的headers,urllib僅可以接受URL。這意味著,你不可以偽裝你的User Agent字符串等。

5 Post和Get區別

GET後退按鈕/刷新無害,POST數據會被重新提交(瀏覽器應該告知用戶數據會被重新提交)。

GET書籤可收藏,POST為書籤不可收藏。

GET能被緩存,POST不能緩存 。

GET編碼類型application/x-www-form-url,POST編碼類型encodedapplication/x-www-form-urlencoded 或 multipart/form-data。為二進制數據使用多重編碼。

GET歷史參數保留在瀏覽器歷史中。POST參數不會保存在瀏覽器歷史中。

GET對數據長度有限制,當發送數據時,GET 方法向 URL 添加數據;URL 的長度是受限制的(URL 的最大長度是 2048 個字符)。POST無限制。

GET只允許 ASCII 字符。POST沒有限制。也允許二進制數據。

與 POST 相比,GET 的安全性較差,因為所發送的數據是 URL 的一部分。在發送密碼或其他敏感信息時絕不要使用 GET !POST 比 GET 更安全,因為參數不會被保存在瀏覽器歷史或 web 服務器日誌中。

GET的數據在 URL 中對所有人都是可見的。POST的數據不會顯示在 URL 中。

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 網站用戶密碼保存

明文保存

明文hash後保存,如md5

MD5+Salt方式,這個salt可以隨機

知乎使用了Bcrypy(好像)加密

9 HTTP和HTTPS

HTTPS(全稱:Hypertext Transfer Protocol over Secure Socket Layer),是以安全為目標的HTTP通道,簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。 它是一個URI scheme(抽象標識符體系),句法類同http:體系。用於安全的HTTP數據傳輸。https:URL表明它使用了HTTP,但HTTPS存在不同於HTTP的默認端口及一個加密/身份驗證層(在HTTP與TCP之間)。這個系統的最初研發由網景公司進行,提供了身份驗證與加密通訊方法,現在它被廣泛用於萬維網上安全敏感的通訊,例如交易支付方面。

超文本傳輸協議 (HTTP-Hypertext transfer protocol) 是一種詳細規定了瀏覽器和萬維網服務器之間互相通信的規則,通過因特網傳送萬維網文檔的數據傳送協議。

10 XSRF和XSS

CSRF(Cross-site request forgery)跨站請求偽造

XSS(Cross Site Scripting)跨站腳本攻擊

CSRF重點在請求,XSS重點在腳本

11 RESTful架構(SOAP,RPC)

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

12 SOAP

SOAP(原為Simple Object Access Protocol的首字母縮寫,即簡單對象訪問協議)是交換數據的一種協議規範,使用在計算機網絡Web服務(web service)中,交換帶結構信息。SOAP為了簡化網頁服務器(Web Server)從XML數據庫中提取數據時,節省去格式化頁面時間,以及不同應用程序之間按照HTTP通信協議,遵從XML格式執行資料互換,使其抽象於語言實現、平臺和硬件。

13 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

14 CGI和WSGI

CGI是通用網關接口,是連接web服務器和應用程序的接口,用戶通過CGI來獲取動態數據或文件等。

CGI程序是一個獨立的程序,它可以用幾乎所有語言來寫,包括perl,c,lua,python等等。

WSGI, Web Server Gateway Interface,是Python應用程序或框架和Web服務器之間的一種接口,WSGI的其中一個目的就是讓用戶可以用統一的語言(Python)編寫前後端。

16 中間人攻擊

在GFW裡屢見不鮮的,呵呵.

中間人攻擊(Man-in-the-middle attack,通常縮寫為MITM)是指攻擊者與通訊的兩端分別創建獨立的聯繫,並交換其所收到的數據,使通訊的兩端認為他們正在通過一個私密的連接與對方直接對話,但事實上整個會話都被攻擊者完全控制。

17 c10k問題

所謂c10k問題,指的是服務器同時支持成千上萬個客戶端的問題,也就是concurrent 10 000 connection(這也是c10k這個名字的由來)。

18 socket

Socket=Ip address+ TCP/UDP + port

19 瀏覽器緩存

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

瀏覽器緩存機制,其實主要就是HTTP協議定義的緩存機制(如: Expires; Cache-control等)

Expires策略

Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器緩存取數據,而無需再次請求。

Cache-control策略(重點關注)

Cache-Control與Expires的作用一致,都是指明當前資源的有效期,控制瀏覽器是否直接從瀏覽器緩存取數據還是重新發請求到服務器取數據。只不過Cache-Control的選擇更多,設置更細緻,如果同時設置的話,其優先級高於Expires

20 HTTP1.0和HTTP1.1

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

請求頭Host字段,一個服務器多個網站

長鏈接

文件斷點續傳

身份認證,狀態管理,Cache緩存

21 Ajax

AJAX,Asynchronous JavaScript and XML(異步的 JavaScript 和 XML), 是與在不重新加載整個頁面的情況下,與服務器交換數據並更新部分網頁的技術。

數據結構

1 紅黑樹

紅黑樹與AVL的比較:

AVL是嚴格平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多;

紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低;

所以簡單說,如果你的應用中,搜索的次數遠遠大於插入和刪除,那麼選擇AVL,如果搜索,插入刪除次數幾乎差不多,應該選擇RB。

1 臺階問題/斐波納挈

一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

1

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)

第三種方法

1

2

3

4

5

def fib(n):

a, b = 0, 1

for _ in xrange(n):

a, b = b, a + b

return b

2 變態臺階問題

一隻青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

1

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

3 矩形覆蓋

我們可以用21的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個21的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?

第2n個矩形的覆蓋方法等於第2(n-1)加上第2*(n-2)的方法。

1

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

4 楊氏矩陣查找

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

5 去除列表中的重複元素

用集合

1

list(set(l))

用字典

1

2

3

l1 = ['b','c','d','b','c','a','a']

l2 = {}.fromkeys(l1).keys()

print l2

用字典並保持順序

1

2

3

4

l1 = ['b','c','d','b','c','a','a']

l2 = list(set(l1))

l2.sort(key=l1.index)

print l2

1

2

3

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 直接創建

1

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

2 工廠方法

1

2

3

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

dict2=dict(items)

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

3 fromkeys()方法

1

2

3

4

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

1

2

3

4

5

6

7

8

9

10

11

12

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 找零問題

defcoinChange(values, money, coinsUsed):

valuesT[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 求最大樹深

1

2

3

4

def maxDepth(root):

if not root:

return 0

return max(maxDepth(root.left), maxDepth(root.right)) + 1

19 求兩棵樹是否相同

1

2

3

4

5

6

7

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 前序中序求後序

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

Python Web相關

解釋一下 WSGI 和 FastCGI 的關係?

CGI全稱是“公共網關接口”(CommonGateway Interface),HTTP服務器與你的或其它機器上的程序進行“交談”的一種工具,其程序須運行在網絡服務器上。 CGI可以用任何一種語言編寫,只要這種語言具有標準輸入、輸出和環境變量。如php,perl,tcl等。

FastCGI像是一個常駐(long-live)型的CGI,它可以一直執行著,只要激活後,不會每次都要花費時間去fork一次(這是CGI最為人詬病的fork-and-execute模式)。它還支持分佈式的運算, 即 FastCGI 程序可以在網站服務器以外的主機上執行並且接受來自其它網站服務器來的請求。

FastCGI是語言無關的、可伸縮架構的CGI開放擴展,其主要行為是將CGI解釋器進程保持在內存中並因此獲得較高的性能。眾所周知,CGI解釋器的反覆加載是CGI性能低下的主要原因,如果CGI解釋器保持在內存中並接受FastCGI進程管理器調度,則可以提供良好的性能、伸縮性、Fail- Over特性等等。

WSGI的全稱為: PythonWeb Server Gateway Interface v1.0 (Python Web 服務器網關接口),

它是 Python 應用程序和 WEB 服務器之間的一種接口。

它的作用,類似於FCGI 或 FASTCGI 之類的協議的作用。

WSGI 的目標,是要建立一個簡單的普遍適用的服務器與 WEB 框架之間的接口。

Flup就是使用 Python 語言對 WSGI 的一種實現,是可以用於 Python 的應用開發中的一種工具或者說是一種庫。

Spawn-fcgi是一個小程序,這個程序的作用是管理fast-cgi進程,那麼管理wsgi進程也是沒有問題的,功能和php-fpm類似。

故,簡單地說,WSGI和FastCGI都是一種CGI,用於連接WEB服務器與應用程序,而WSGI專指Python應用程序。而flup是WSGI的一種實現,Spawn-fcgi是用於管理flup進程的一個工具,可以啟動多個wsgi進程,並管理它們。

解釋一下 Django 和 Tornado 的關係、差別

Django源自一個在線新聞 Web站點,於 2005 年以開源的形式被釋放出來。

Django 框架的核心組件有:

用於創建模型的對象關係映射為最終用戶設計的完美管理界面一流的 URL 設計設計者友好的模板語言緩存系統等等

它鼓勵快速開發,並遵循MVC設計。Django遵守 BSD版權,最新發行版本是Django

1.4,於2012年03月23日發佈.Django的主要目的是簡便、快速的開發數據庫驅動的網站。它強調代碼複用,多個組件可以很方便的以“插件”形式服務於整個框架,Django有許多功能強大的第三方插件,你甚至可以很方便的開發出自己的工具包。這使得Django具有很強的可擴展性。它還強調快速開發和DRY(Do Not RepeatYourself)原則。

Tornado是 FriendFeed使用的可擴展的非阻塞式 web 服務器及其相關工具的開源版本。這個 Web 框架看起來有些像 web.py 或者 Google 的 webapp,不過為了能有效利用非阻塞式服務器環境,這個 Web 框架還包含了一些相關的有用工具和優化。

Tornado 和現在的主流 Web 服務器框架(包括大多數Python 的框架)有著明顯的區別:它是非阻塞式服務器,而且速度相當快。得利於其 非阻塞的方式和對epoll的運用,Tornado 每秒可以處理數以千計的連接,這意味著對於實時 Web服務來說,Tornado 是一個理想的 Web 框架。我們開發這個 Web 服務器的主要目的就是為了處理 FriendFeed 的實時功能 ——在 FriendFeed 的應用裡每一個活動用戶都會保持著一個服務器連接。(關於如何擴容 服務器,以處理數以千計的客戶端的連接的問題。

解釋下django-debug-toolbar的使用

使用django開發站點時,可以使用django-debug-toolbar來進行調試。在settings.py中添加’debug_toolbar.middleware.DebugToolbarMiddleware’到項目的MIDDLEWARE_CLASSES 內。

解釋下Django使用redis緩存服務器

為了能在Django中使用redis,還需要安裝redis for Django的插件。然後在Django的settings中配置了。現在連接和配置都已經完成了,接下來是一個簡單的例子:

from django.conf import settings

from django.core.cache import cache

read cache user id

def read_from_cache(self, user_name):

key = 'user_id_of_'+user_name

value = cache.get(key)

if value == None:

data = None

else:

data = json.loads(value)

return data

write cache user id

def write_to_cache(self, user_name):

key = 'user_id_of_'+user_name

cache.set(key, json.dumps(user_name), settings.NEVER_REDIS_TIMEOUT)

如何進行Django單元測試

Django的單元測試使用python的unittest模塊,這個模塊使用基於類的方法來定義測試。類名為django.test.TestCase,繼承於python的unittest.TestCase。

from django.test import TestCase

from myapp.models import Animal

class AnimalTestCase(TestCase):

def setUp(self):

Animal.objects.create(name="lion", sound="roar")

Animal.objects.create(name="cat", sound="meow")

def test_animals_can_speak(self):

"""Animals that can speak are correctly identified"""

lion = Animal.objects.get(name="lion")

cat = Animal.objects.get(name="cat")

self.assertEqual(lion.speak(), 'The lion says "roar"')

self.assertEqual(cat.speak(), 'The cat says "meow"')

執行目錄下所有的測試(所有的test*.py文件):運行測試的時候,測試程序會在所有以test開頭的文件中查找所有的test cases(inittest.TestCase的子類),自動建立測試集然後運行測試。

1

$ python manage.py test

執行animals項目下tests包裡的測試:

$ python manage.py testanimals.tests

執行animals項目裡的test測試:

1

$ python manage.py testanimals

單獨執行某個test case:

1

$ python manage.py testanimals.tests.AnimalTestCase

單獨執行某個測試方法:

1

$ python manage.py testanimals.tests.AnimalTestCase.test_animals_can_speak

為測試文件提供路徑:

1

$ python manage.py testanimals/

通配測試文件名:

1

$ python manage.py test--pattern="tests_*.py"

啟用warnings提醒:

1

$ python -Wall manage.py test

解釋下Http協議

HTTP是一個屬於應用層的面向對象的協議,由於其簡捷、快速的方式,適用於分佈式超媒體信息系統。

HTTP協議的主要特點可概括如下:

1.支持客戶/服務器模式。

2.簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯繫的類型不同。由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。

3.靈活:HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。

4.無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。採用這種方式可以節省傳輸時間。

5.無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。

解釋下Http請求頭和常見響應狀態碼

Accept:指瀏覽器或其他客戶可以接愛的MIME文件格式。可以根據它判斷並返回適當的文件格式。

Accept-Charset:指出瀏覽器可以接受的字符編碼。英文瀏覽器的默認值是ISO-8859-1.

Accept-Language:指出瀏覽器可以接受的語言種類,如en或en-us,指英語。

Accept-Encoding:指出瀏覽器可以接受的編碼方式。編碼方式不同於文件格式,它是為了壓縮文件並加速文件傳遞速度。瀏覽器在接收到Web響應之後先解碼,然後再檢查文件格式。

Cache-Control:設置關於請求被代理服務器存儲的相關選項。一般用不到。

Connection:用來告訴服務器是否可以維持固定的HTTP連接。HTTP/1.1使用Keep-Alive為默認值,這樣,當瀏覽器需要多個文件時(比如一個HTML文件和相關的圖形文件),不需要每次都建立連接。

Content-Type:用來表名request的內容類型。可以用HttpServletRequest的getContentType()方法取得。

Cookie:瀏覽器用這個屬性向服務器發送Cookie。Cookie是在瀏覽器中寄存的小型數據體,它可以記載和服務器相關的用戶信息,也可以用來實現會話功能。

狀態代碼有三位數字組成,第一個數字定義了響應的類別,且有五種可能取值:

1xx:指示信息–表示請求已接收,繼續處理

2xx:成功–表示請求已被成功接收、理解、接受

3xx:重定向–要完成請求必須進行更進一步的操作

4xx:客戶端錯誤–請求有語法錯誤或請求無法實現

5xx:服務器端錯誤–服務器未能實現合法的請求

常見狀態代碼、狀態描述、說明:

200 OK//客戶端請求成功

400 Bad Request //客戶端請求有語法錯誤,不能被服務器所理解

401 Unauthorized //請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用

403 Forbidden //服務器收到請求,但是拒絕提供服務

404 Not Found //請求資源不存在,eg:輸入了錯誤的URL

500 Internal Server Error //服務器發生不可預期的錯誤

503 Server Unavailable //服務器當前不能處理客戶端的請求,一段時間後可能恢復正常

eg:HTTP/1.1 200 OK (CRLF)

爬蟲

一、試列出至少三種目前流行的大型數據庫的名稱:________、_________、__________,其中您最熟悉的是__________,從__________年開始使用。

Oracle,Mysql,SQLServer Oracle根據自己情況

二、有表List,並有字段A、B、C,類型都是整數。表中有如下幾條記錄:

A

B

C

2

7

9

5

6

4

3

11

9

現在對該表一次完成以下操作:

查詢出B和C列的值,要求按B列升序排列

寫出一條新的記錄,值為{7,9,8}

查詢C列,要求消除重複的值,按降序排列

寫出完成完成以上操作的標準的SQL語句,並且寫出操作3的結果。

create table List(A int,B int,C int)

Select B,C from List order by B

Insert into List values(7,9,8)

Select distinct(C) from List order by desc;

984

三、請簡要說明視圖的作用

1.數據庫視圖隱藏了數據的複雜性。

2.數據庫視圖有利於控制用戶對錶中某些列的訪問。

3.數據庫視圖使用戶查詢變得簡單。

四、列舉您使用過的python網絡爬蟲所用到的網絡數據包(最熟悉的在前):

requests、urllib、urllib2、httplib2

五、列舉您使用過的python網絡爬蟲所用到的解析數據包(最熟悉的在前):

BeautifulSoup、pyquery、Xpath、lxml

六、列舉您使用過的python中的編碼方式(最熟悉的在前):

UTF-8,ASCII,gbk

七、python3.5語言中enumerate的意思是_______________________

對於一個可迭代的(iterable)/可遍歷的對象(如列表、字符串),enumerate將其組成一個索引序列,利用它可以同時獲得索引和值

enumerate多用於在for循環中得到計數

八、99的八進制表示是_______________________

143

九、請舉出三種常用的排序算法

冒泡、選擇、快速

十、列出比較熟悉的爬蟲框架

Scrapy

十一、用4、9、2、7四個數字,可以使用+、-、*和/,每個數字使用一次,使表達式的結果為24,表達式是_____________________________

(9+7-4)*2

十二、對你最有影響的或是您認為最有價值的軟件方面的幾本書是?

十三、您最熟悉的Unix環境是_____________.Unix下查詢環境變量的命令是________,查詢腳本定時任務的命令是____________________

1AIX,envcrontab

十四、寫出在網絡爬蟲爬取數據的過程中,遇到的防爬蟲問題的解決方案

通過headers反爬蟲:解決策略,偽造headers

基於用戶行為反爬蟲:動態變化去爬取數據,模擬普通用戶的行為

基於動態頁面的反爬蟲:跟蹤服務器發送的ajax請求,模擬ajax請求

十五、閱讀以下Python程序

foriinrange(5,0,-1):

print(i)

請在下面寫出打印結果

54321

十六、在某系統中一個整數佔用兩個八位字節,使用Python按下面的要求編寫完整程序。

接收從標準輸入中依次輸入的五個數字,將其組合成為一個整數,放入全局變量n中,隨後在標準輸出輸出這個整數。(ord(char)獲取字符ASCII值的函數)


"/<pivot>


分享到:


相關文章: