Python后端工程師面試題
之前在網上搜集python面試題,這是最流行的一個版本。但是沒有答案,故自己搜集或回答了一部分。現分享給大家,有些題目
回答的並不准確,望各路大神糾正,完善!!!
python語法以及其他基礎部分
1.可變與不可變類型;
不可變類型(數字、字符串、元組、不可變集合)不可變類型不能被修改。
可變類型(列表、字典、可變集合)
2.淺拷貝與深拷貝的實現方式、區別;deepcopy如果你來設計,如何實現;
基本類型 :
基本類型在內存中分別占有固定大小的空間,他們的值保存在棧空間,我們是通過按值來訪問的。
引用類型 :
引用類型,值大小不固定,棧內存中存放地址指向堆內存中的對象。是按引用訪問的。
棧內存中存放的只是該對象的訪問地址,在堆內存中為這個值分配空間。由於這種值的大小不固定,
因此不能把它們保存到棧內存中。但內存地址大小的固定的,因此可以將內存地址保存在棧內存中。
這樣,當查詢引用類型的變量時, 先從棧中讀取內存地址, 然后再通過地址找到堆中的值。對於這種,
我們把它叫做按引用訪問。
淺拷貝只復制指向某個對象的引用地址,而不復制對象本身(只復制一層對象的屬性),新舊對象還是共享同一塊內存。
但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。
淺復制只復制一層對象的屬性,而深復制則遞歸復制了所有層級。
import copy as cp
L=[1,[2,3]]
L1=cp.copy(L) #淺拷貝: 只是引用,並不會開辟新的內存空間,
L1與L指向同一片內存空間。L改變,L1也會鎖着改變。
L2=cp.deepcopy(L) #深拷貝:會為數據重新開辟一片內存空間,L的變化不會影響L2。
3.new() 與 init()的區別;
_new_作用於_init_之前。前者可以決定是否調用后者,或者說可以決定調用哪個類的_init_方法。
首先要知道在面向對象編程中,實例化基本遵循創建實例對象、初始化實例對象、最后返回實例對象這么一個過程。
Python 中的 new 方法負責創建一個實例對象,init 方法負責將該實例對象進行初始化;
4.你知道幾種設計模式;
單例模式:保證一個類僅有一個實例,並提供一個訪問他的全局訪問點,例如框架中的數據庫連接
裝飾器模式:不修改元類代碼和繼承的情況下動態擴展類的功能,例如框架中的每個controller文件會提供before和after方法。
迭代器模式: 提供一個方法順序訪問一個聚合對象中各個元素,在PHP中將繼承 Iterator 類
命令模式: 將”請求”封閉成對象, 以便使用不同的請求,隊列或者日志來參數化其他對象. 命令模式也支持可撤銷的操作.
5.編碼和解碼你了解過么;
數據通過編碼decode轉換成Unicode編碼,編碼的同時會將數據變成byte類型,通過解碼encode轉換為utf-8。
6.列表推導list comprehension和生成器的優劣;
[expr for iter_var in iterable] 列表推導式是將所有的值一次性加載到內存中
(expr for iter_var in iterable)
在大數據量處理時,生成器表達式的優勢就體現出來了,因為它的內存使用方式更好,
效率更高,它並不創建一個列表,只是返回一個生成器。
7.什么是裝飾器;如果想在函數之后進行裝飾,應該怎么做;
裝飾器是一個函數,這個函數的主要作用是包裝另一個函數或類
包裝的目的是在不改變原函數名的情況下改變被包裝對象的行為。
接收一個函數,內部對其包裝,然后返回一個新函數,這樣子動態的增強函數功能
通過高階函數傳遞函數參數,新函數添加舊函數的需求,然后執行舊函數。
8.手寫個使用裝飾器實現的單例模式;
該模式的主要目的是確保某一個類只有一個實例存在。
from functools import warps
def My_decorate(f):
@warps(f)
def fn(args,**kwargs):
print('decorate called')
return f(args,**kwargs)
return fn
@My_decorate
def fx():
pring('fx called')
fx()
9.使用裝飾器的單例和使用其他方法的單例,在后續使用中,有何區別;
使用裝飾器單例屬性不會被覆蓋。因為裝飾器單例模式是直接返回之前生成的對象,
並不會重新初始化對象。像new方法構建的單例模式會重新調用init方法,為實例重新初始化屬性。
10.手寫:正則郵箱地址;
pattern = '[a-zA-Z0-9_.-]+@[a-zA-Z0-9]+.[a-z]'
匹配身份證:
pattern = '(\d{15}$)|(\d{18}$)|(^\d{17}(\d|X|x)$)'
pattern = '[1]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$'
11.介紹下垃圾回收:引用計數/分代回收/孤立引用環;
垃圾回收:python解釋器對正在使用的對象保持計數,當某個對像的引用計數降為0時,垃圾收集器就可以釋放
該對象,獲取分配的內存。當分配對象和取消分配對象的差值高於閾值時垃圾回收才會啟動。
分代回收:python將所有的對象分為0,1,2三代。所有的新建對象都是0代對象。當某一代對象經歷過垃圾回收,
依然存活,那么它就被歸入下一代對象。垃圾回收啟動時,一定會掃描所有的0代對象。
如果0代經過一定次數垃圾回收,那么就啟動對0代和1代的掃描清理。
當1代也經歷了一定次數的垃圾回收后,那么會啟動對0,1,2,即對所有對象進行掃描。
孤立引用環:
12.多進程與多線程的區別;CPU密集型適合用什么;
多線程:在單個程序中同時運行多個線程完成不同的工作,稱為多線程。
線程共享內存空間;進程的內存是獨立的,
同一個進程的線程之間可以直接交流;兩個進程想通信,必須通過一個中間代理來實現,
一個線程可以控制和操作同一進程里的其他線程;但是進程只能操作子進程
優缺點:1.多進程的優點是穩定性好,一個子進程崩潰了,不會影響主進程以及其余進程。
但是缺點是創建進程的代價非常大,因為操作系統要給每個進程分配固定的資源。
2.多線程優點是效率較高一些,但是致命的缺點是任何一個線程崩潰都可能
造成整個進程的崩潰,因為它們共享了進程的內存資源池。
*CPU密集型適合用多進程開發
13.進程通信的方式有幾種;
進程間通信主要包括管道, 系統IPC(包括消息隊列,信號量,共享存儲), SOCKET
14.介紹下協程,為何比線程還快;
高並發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用於高並發處理
協程能保留上一次調用時的狀態,管是進程還是線程,每次阻塞、切換都需要陷入系統調用,
使用線程時需要非常小心地處理同步問題,而協程完全不存在這個問題。
15.range和xrange的區別
xrange和range 的用法完全相同,但是返回的是一個生成器。
算法排序部分
16.手寫快排;堆排;幾種常用排序的算法復雜度是多少;快排平均復雜度多少,最壞情況如何優化;
17.手寫:已知一個長度n的無序列表,元素均是數字,要求把所有間隔為d的組合找出來,你寫的解法算法復雜度多少;
def func(x, d):
L = []
n = len(x)
if d > n:
return None
L.append(x[0])
a = d + 1
while a < n:
L.append(x[a])
a = a + d + 1
return L
list = [x for x in range(100)]
result = func(list, 10)
print(result)
我寫的只考慮到從第一個元素開始組合。沒有考慮從第二個,第三個元素開始......求大神提供完全正確方法。
18.手寫:一個列表A=[A1,A2,…,An],要求把列表中所有的組合情況打印出來;
19.手寫:用一行python寫出1+2+3+…+108 ;
s = sum([x for x in range(1,108+1)])
20.手寫python:用遞歸的方式判斷字符串是否為回文;
def isHuiWen(str):
if(len(str) <2):
return True
if str[0] !=str[-1]:
return False
return isHuiWen(str[1:-1])
str = input("請輸入一個字符串:")
if isHuiWen(str):
print("該字符串為回文字符串")
else:
print("該字符串不是回文")
21.單向鏈表長度未知,如何判斷其中是否有環;
22.單向鏈表如何使用快速排序算法進行排序;
23.手寫:一個長度n的無序數字元素列表,如何求中位數,如何盡快的估算中位數,
你的算法復雜度是多少;
def func(list):
if len(list) <= 1:
return list
L = sorted(list)
n = len(list) % 2
m = len(list) // 2
if n == 0:
s = (L[m] + L[m-1]) / 2
return s
s = L[m]
return s
l = [1,2,6,84,12,62,100]
print(func(l))
24.如何遍歷一個內部未知的文件夾(兩種樹的優先遍歷方式)
網絡基礎部分
25.TCP/IP分別在模型的哪一層;
TCP 在傳輸層,
IP在網絡層
26.socket長連接是什么意思;
在一個TCP連接上可以連續發送多個數據包,在TCP連接保持期間,如果沒有數據包發送,
需要雙方發檢測包以維持此鏈接,一般需要自己做在線維持。
長連接指建立SOCKET連接后不管是否使用都保持連接,但安全性較差
數據庫的連接用長連接。如果用短連接頻繁的通信會造成socket錯誤,
而且頻繁的socket創建也是對資源的浪費
27.select和epoll你了解么,區別在哪;
28.TCP UDP區別;三次握手四次揮手講一下;
TCP協議: (在傳輸之前先建立連接)面向連接(的通信服務):可靠地數據傳輸,失序,無差錯,無丟失,無重復
用途:適用於傳輸內容比較大,網絡情況比較好,需要提供准確的情況.比如:聊天信息,文字傳輸,郵件傳輸。
udp 協議:面向無連接的服務,不可靠,發送時由發送端自主進行,不考慮接收端。
用途:適用於網絡較差,對傳輸准確性要求低,廣播組播.比如:視頻會議,廣播數據。
三次握手:
1.客戶端向服務器發起鏈接請求(問是否可以連接)
2.服務器接受到請求后進行確認(允許連接)返回報文
3.客戶端收到許可,建立連接
四次揮手:
1.主動方發送報文告知被動方要斷開連接
2.被動發返回報文沒告知收到請求,准備斷開
3.被動發發送報文給主動方告知准備就緒可以斷開
4.主動方發送報文確定斷開
29.TIME_WAIT過多是因為什么;
30.http一次連接的全過程:你來說下從用戶發起request——到用戶接收到response;
域名解析 --> 發起TCP的3次握手 --> 建立TCP連接后發起http請求 --> 服務器響應http請求,
瀏覽器得到html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等)
--> 瀏覽器對頁面進行渲染呈現給用戶.
參考大神文章:https://www.cnblogs.com/engeng/articles/5959335.html
31.http連接方式。get和post的區別,你還了解其他的方式么;
get是從服務器上獲取數據,post是向服務器傳送數據。在客戶端, get方式在通過URL提交數據,
數據在URL中可以看到;post方式,數據放置在HTML HEADER內提交。
對於get方式,服務器端用Request.QueryString獲取變量的值,
對於post方式,服務器端用Request.Form獲取提交的數據。
Get 方式提交數據,會帶來安全問題,比如一個登陸頁面,通過 Get 方式提交數據時,
用戶名和密碼將出現在 URL 上,如果頁面可以被緩存或者其他人可以訪問客戶這台機器,
就可以從歷史記錄獲得該用戶的帳號和密碼,所以表單提交建議使用 Post 方法。
32.restful你知道么;
33.狀態碼你知道多少,比如200/403/404/504等等;
200:操作成功。
403:訪問受限,授權過期(錯誤提示)
404:資源,服務器未找到
504:網關超時,服務器作為網關或代理,但是沒有及時從上游服務器收到請求。
1-9 ↩︎