Python后端工程師面試題


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,10
8+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. 1-9 ↩︎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM