python 一些面試題整理(一)

2020-08-12 22:47:47

python基礎
python2和python3的區別
1.效能
Py3.0執行 pystone benchmark的速度比Py2.5慢30%。Guido認爲Py3.0有極大的優化空間,在字串和整形操作上可以取得很好的優化結果。
Py3.1效能比Py2.5慢15%,還有很大的提升空間。
2.編碼
Py3.X原始碼檔案預設使用utf-8編碼
3. 語法
1)去除了<>,全部改用!=
2)去除``,全部改用repr()
3)關鍵詞加入as 和with,還有True,False,None
4)整型除法返回浮點數,要得到整型結果,請使用//
5)加入nonlocal語句。使用noclocal x可以直接指派外圍(非全域性)變數
6)去除print語句,加入print()函數實現相同的功能。同樣的還有 exec語句,已經改爲exec()函數
7)改變了順序操作符的行爲,例如x<y,當x和y型別不匹配時拋出TypeError而不是返回隨即的 bool值
8)輸入函數改變了,刪除了raw_input,用input代替:
2.X:guess = int(raw_input('Enter an integer : ')) # 讀取鍵盤輸入的方法
3.X:guess = int(input('Enter an integer : '))
9)去除元組參數解包。不能def(a, (b, c)):pass這樣定義函數了
10)新式的8進位制字變數,相應地修改了oct()函數。
11)增加了 2進位制字面量和bin()函數
12)擴充套件的可迭代解包。在Py3.X 裡,a, b, *rest = seq和 *rest, a = seq都是合法的,只要求兩點:rest是list
物件和seq是可迭代的。
13)新的super(),可以不再給super()傳參數,
14)新的metaclass語法:
class Foo(*bases, **kwds):
pass
15)支援class decorator。用法與函數decorator一樣:
4. 字串和位元組串
1)現在字串只有str一種型別,但它跟2.x版本的unicode幾乎一樣。
2)關於位元組串,請參閱「數據型別」的第2條目
5.數據型別
1)Py3.X去除了long型別,現在只有一種整型——int,但它的行爲就像2.X版本的long
2)新增了bytes型別,對應於2.X版本的八位串,定義一個bytes字面量的方法如下:
str物件和bytes物件可以使用.encode() (str -> bytes) or .decode() (bytes -> str)方法相互轉化。
3)dict的.keys()、.items 和.values()方法返回迭代器,而之前的iterkeys()等函數都被廢棄。同時去掉的還有 dict.has_key(),用 in替代它吧

Python的數據型別
可變型別:列表,字典,集合
不可變型別:整形,字串,元祖
string.replace(str1,str2)把 string 中的 str1 替換成 str2,如果 num 指定
string.capitalize()把字串的第一個字元大寫
string.count(str, beg=0, end=len(string))返回 str 在 string 裏面出現的次數,如果 beg 或者 end 指定則返回指定範圍內 str 出現的次數

列表:
列表索引從0開始,你可以對列表的數據項進行修改或更新,你也可以使用append()方法來新增列表項,可以使用 del 語句來刪除列表的的元素,列表對 + 和 * 的操作符與字串相似。+ 號用於組合列表,* 號用於重複列表。列表擷取與字串操作型別,二元列表,深淺拷貝,

list.sort( key=None, reverse=False) 對原列表進行排序
list.remove(obj)移除列表中某個值的第一個匹配項
list.append()列表新增元素
list.index()從列表中找出某個值第一個匹配項的索引位置
列表推導式: [i for i in range(10) if i % 2 == 0]
用字串和下標生成字典
a = ‘1sdf234saf’
dict = {}
for index,value in enumerate(list(a)):
dict[index] = value
print(dict)
print({index:value for index,value in enumerate(list(a))})
用字串和ascii生成字典
a = ‘ablkasdjlasfd’
b = {}
for index in list(a):
b[index] = ord(index)
print(b)
print({index:ord(value) for index,value in enumerate(list(a))})
python常用的標準庫和第三方庫
標準庫:
os:提供了不少與操作系統相關聯的函數
re: 正則匹配
sys: 通常用於命令列參數
time時間, random隨機, threading執行緒,multiprocessing進程,
第三方庫:
pillow,requests,responses,redis,PyMySQL,scrapy,celery,SQLAlchemy,MySQL

GIL 全域性直譯器鎖
是計算機程式設計語言直譯器,用於同步執行緒的一種機制 機製,在任何時刻只有一個執行緒在執行,即便在多核心的處理器上,使用GIL的直譯器也只允許同一時間執行一個執行緒

*args **kwagrs

不知道向函數傳遞多少參數時,傳遞列表或者元組使用 *args

不知道傳遞多少關鍵字參數,使用**kwargs

正則表達式匹配中,(.*)和(.*?)匹配區別?

(.*)是貪婪匹配,會把滿足正則的儘可能多的往後匹配

(.*?)是非貪婪匹配,會把滿足正則的儘可能少匹配

python垃圾回收機制 機製

python垃圾回收主要以參照計數爲主,標記-清除和分代清除爲輔的機制 機製,其中標記-清除和分代回收主要是爲了處理回圈參照的難題。
參照計數演算法
當有1個變數儲存了物件的參照時,此物件的參照計數就會加1
當使用del刪除變數指向的物件時,如果物件的參照計數不爲1,比如3,那麼此時只會讓這個參照計數減1,即變爲2,當再次呼叫del時,變爲1,如果再呼叫1次del,此時會真的把物件進行刪除

深拷貝和淺拷貝總結:

淺拷貝:(copy)
對於不可變型別 Number 、String 、Tuple,淺拷貝僅僅是地址的指向,並不會開闢新的地址空間。
對於可變數據型別 List 、Dictionary 、Set,淺拷貝會開闢新的地址空間(僅僅是最頂層開闢新的地址空間,裏面的元素地址還是一樣的),進行淺拷貝。
淺拷貝後,改變原始物件中可變數據型別裏面元素的值,會同時影響拷貝物件的元素的值;改變原始物件中不可變數據型別裏面元素的值,只有原始物件裏面的值會改變。(操作拷貝物件對原始物件的也是同理)

深拷貝(deepcopy)
在深拷貝中,對於不可變數據型別 Number 、String 、Tuple,深拷貝仍然是地址的指向,並不會開闢新的地址空間。
對於可變數據型別 List 、Dictionary 、Set,深拷貝會開闢新的地址空間(最頂層地址和裏面的元素地址都會開闢新的地址空間),進行深拷貝。
深拷貝後,改變原始物件中的值(不區分可變型別和不可變型別),只有原始物件中的值受影響,拷貝物件不會被影響;同理,改變拷貝物件中的值(不區分可變型別和不可變型別),只有拷貝物件中的值受影響,原始物件不會被影響。

lambda匿名函數的格式

冒號前是參數,可以有多個,用逗號隔開,冒號右邊的爲表達式。其實lambda返回值是一個函數的地址,也就是函數物件。

lambda 函數是匿名的,有輸入和輸出,擁有自己的名稱空間

你對中介軟體的看法,可以改變輸入輸出嗎?

中介軟體的作用就是攔截過濾,肯定是可以改變請求輸入和響應輸出的,例如可以用中介軟體將請求參數值中一些不安全的字元替換掉,做無害化處理;響應的時候,可以對超長內容進行截斷處理,這些就是改變輸入輸出的情況

Python可以提高效能的方法

1、使用生成器,因爲可以節約大量記憶體
2、回圈程式碼優化,避免過多重複程式碼的執行
3、核心模組用Cython PyPy等,提高效率
4、多進程、多執行緒、協程
5、多個if elif條件判斷,可以把最有可能先發生的條件放到前面寫,這樣可以減少程式判斷的次數,提高效率

python中怎樣向檔案中寫入內容(對檔案的操作)

1.開啓python檔案,在檔案中設定一個變數,用於儲存要寫入內容的檔案的路徑。
2.通過open方法來開啓檔案,第一個參數是檔案的路徑,第二個參數是開啓的模式,這裏設定爲w模式。
3.上面的w模式,在寫入的時候,會將原來的檔案內容覆蓋掉,如果你不希望原來的檔案內容被覆蓋,可以使用a的模式,表示追加寫入!
4.現在開始寫入內容,用write方法進行寫入,write裏面的參數就是要寫入的內容,內容自己定義這裏輸入一個字串作爲測試!
5.當我們寫檔案的程式碼執行到最後的時候,一定要記得關閉檔案,用close函數來表,示如果不關閉檔案,會導致有些內容沒有寫進去的情況!

python異常捕捉
偵錯Python程式時,經常會報出一些異常,異常的原因一方面可能是寫程式時由於疏忽或者考慮不全造成了錯誤,這時就需要根據異常Traceback到出錯點,進行分析改正;另一方面,有些異常是不可避免的,但我們可以對異常進行捕獲處理,防止程式終止

try:
    <語句>#寫一個函數,判斷傳入的數值是否符合預期
except Exception:
    print('異常說明')

單元測試
單元測試-針對程式中最小的功能單元所進行的測試
在Python程式中最小的功能單元是函數或者方法
白盒測試 - 知道函數的內部結構-保證程式碼的每條執行路徑,每個分支,每個語句最終都被覆蓋到
黑箱測試-不知道函數的內部結構-呼叫函數施加輸入看實際的輸出跟預期的輸出是否完全一致

~劃分等價類/邊界值/因果圖===》《軟體測試的藝術》
Python中的unittest模組可以輔助我們編寫測試用例--->TestCase—>test_xxx方法
在測試方法中可以使用斷言語句來判斷實際輸出是否跟預期輸出匹配-》AssertionError
python-m unittest interview04.py 執行摸個模組
python-m unittest interview04.TestInterview03 執行摸個模組裡的類
python-m unittest interview04.TestInterview03.test_with_same_string 執行摸個模組裡的類的摸個方法
建議大家使用pytest或者nose2來跑單元測試

import unittest
from unittset_demo.demo1 import MyClass


class MyclassTest(unittest.TestCase):
    def setUp(self) -> None:
        '''
        測試之前的準備工作
        :return:
        '''
        self.clac = MyClass(4,3)
    def tearDown(self) -> None:
        '''
        測試之後的收尾
        如關閉數據庫
        :return:
        '''
        pass
    def test_add(self):
        ret = self.clac.add()
        self.assertEqual(ret,9)  
    def test_sub(self):
        ret = self.clac.sub()
        self.assertEqual(ret,-1)
if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(MyclassTest('test_add'))
    suite.addTest(MyclassTest('test_sub'))

    runner = unittest.TextTestRunner()
    runner.run(suite)

寫一個單列模式
因爲建立物件時__new__方法執行,並且必須return 返回範例化出來的物件所cls.__instance是否存在,不存在的話就建立物件,存在的話就返回該物件,來保證只有一個範例物件存在(單列),列印ID,值一樣,說明物件同一個

class Foo(object):
    instance=None
    def __init__(self):
        pass
    @classmethod         # 類方法  類方法只能存取類變數,不能存取範例變數
    def get_instance(self):
        if Foo.instance:
            return Foo.instance
        else:
            Foo.instance = Foo()
            return Foo.instance
obj1=Foo.get_instance()
obj2=Foo.get_instance()
print(id(obj1),id(obj2))

上下文管理器

實現了上下文管理協定(__ enter __ , __ exit __ )的物件
被@contextmanager裝飾器裝飾的函數

from contextlib import contextmanager
from time import time
@contextmanager
def mytime():
	start = time()
	yield 'hello
	end = time()
	print(f'{end-start}s')
def main():
	with mytime()as msg:
	print(msg)
	for a in range(1234,2000):
		b = 9876-a
		a**b
	with mytime():
		for a in range(6789,9000):
			b=10000-a
			a **b
if __name__ == 'main__':
main()

TCP/IP
傳輸協定,叫作網路通訊協定,它是在網路的使用中的最基本的通訊協定
特點:可靠的,效率地,應用場景,聊天

TCP建立連線的三次握手:
1、用戶端向伺服器端發送一個SYN(建立連線)請求
2、伺服器端收到請求後,給用戶端一個響應
3、用戶端收到伺服器端的響應後,恢復給伺服器端一個確認資訊,計算機既可以互相進行數據傳輸
建立連線的過程,只需要證明你在我也在,然後我們建立連線就可以了

TCP斷開連線的四次揮手:
1、TCP用戶端發送一個FIN(關閉連線),用來關閉客戶到伺服器的數據傳送(請求)
2、伺服器收到這個FIN,它發回一個ACK(響應),確認序號爲收到的序號+1.(等待關閉)狀態(知道了)
3、伺服器端關閉用戶端的連線,發送一個FIN給用戶端。伺服器端進入連線中斷請求的確認狀態(請求斷開連線)
4、用戶端發回ACK報文確認,並將確認序號設定爲收到序號+1.(確認斷開連線)

TCP結束了,數據發送要斷開連線的時候的過程,所以我要確定,我即結束了發送數據,也結束了接受數據,開始用戶端結束了數據發送,他會告知伺服器,但是這個時候用戶端還是可以接受數據的,伺服器收到他結束髮送數據的請求後他只是會停止接受數據但此時伺服器還是可以發送數據的,所以伺服器它也發送完了那就需要你向用戶端發送一次FIN包來告知用戶端,其實我的數據也發送完了,我也要斷開連線了。那此時用戶端收到伺服器這個FIN包後他也會進行一個確認,此時雙方纔會關閉發送和接收的通道。所以斷開連線需要4次揮手。

二、UDP協定:
無連線協定,也稱透明協定,也位於傳輸層。
三、兩者區別:
1) TCP提供面向連接的傳輸,通訊前要先建立連線(三次握手機制 機製); UDP提供無連線的傳輸,通訊前不需要建立連線。
2) TCP提供可靠的傳輸(有序,無差錯,不丟失,不重複); UDP提供不可靠的傳輸,不保證數據可靠完整的到達。
3) TCP傳輸效率比較低; UDP傳輸效率比較高,(IP電話,直播)

HTTP(超文字傳輸協定):短連線
一種無狀態的、應用層的、以請求/應答方式執行的協定

HTTPS協定
HTTPS協定可以理解爲HTTP協定的升級,就是在HTTP的基礎上增加了數據加密。在數據進行傳輸之前,對數據進行加密,然後再發送到伺服器。這樣,就算數據被第三者所截獲,但是由於數據是加密的,所以你的個人資訊讓然是安全的。這就是HTTP和HTTPS的最大區別。
是通過ssL外殼來確保安全性的,主要體現在三方面:
1、數據是加密的SSL協定是通過非對稱密匙分發的形式來完成祕鑰的協商,然後在通過對稱加密的方式對數據完成加密
2、它會去驗證雙方的身份資訊,我們的用戶端和伺服器雙方都需要向CA機構去申請認證書,在SSL握手階段的時候,我們就回去驗證雙方的證書是否可信,從而去驗證雙方的身份,這樣就可以防止第三方的冒充
3、保證數據的完整性每次數據都需要加上MAC的摘要並簽名接收的數據和發送的數據那這個摘要要一樣的,這樣就表示數據並沒有被篡改過

HTTP請求中get和post區別
1、GET請求是通過URL直接請求數據,數據資訊可以在URL中直接看到,比如瀏覽器存取;而POST請求是放在請求頭中的,我們是無法直接看到的;
2、GET提交有數據大小的限制,一般是不超過1024個位元組,而這種說法也不完全準確,HTTP協定並沒有設定URL位元組長度的上限,而是瀏覽器做了些處理,所以長度依據瀏覽器的不同有所不同;POST請求在HTTP協定中也沒有做說明,一般來說是沒有設定限制的,但是實際上瀏覽器也有預設值。總體來說,少量的數據使用GET,大量的數據使用POST。
3、GET請求因爲數據參數是暴露在URL中的,所以安全性比較低,比如密碼是不能暴露的,就不能使用GET請求;POST請求中,請求參數資訊是放在請求頭的,所以安全性較高,可以使用。在實際中,涉及到登錄操作的時候,儘量使用HTTPS請求,安全性更好。

Websocket 長連線

WebSocket是一種在單個TCP連線上進行全雙工通訊的協定
WebSocket使得用戶端和伺服器之間的數據交換變得更加簡單,允許伺服器端主動向用戶端推播數據。在WebSocket API中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向數據傳輸

django實現websocket大致上有兩種方式,一種channels,一種是dwebsocket。channels依賴於redis,twisted等,相比之下使用dwebsocket要更爲方便一些

#dwebsocket有兩種裝飾器:require_websocket和accept_websocekt,使用require_websocket裝飾器會導致檢視函數無法接收導致正常的http請求,一般情況使用accept_websocket方式就可以了,

# dwebsocket的一些內建方法:
# 
# request.is_websocket():判斷請求是否是websocket方式,是返回true,否則返回false
# request.websocket: 當請求爲websocket的時候,會在request中增加一個websocket屬性,
# WebSocket.wait() 返回用戶端發送的一條訊息,沒有收到訊息則會導致阻塞
# WebSocket.read() 和wait一樣可以接受返回的訊息,只是這種是非阻塞的,沒有訊息返回None
# WebSocket.count_messages()返回訊息的數量
# WebSocket.has_messages()返回是否有新的訊息過來
# WebSocket.send(message)像用戶端發送訊息,message爲byte型別

OSI 7 層模型

應用層:程式協定,HTTPS
表現層:資訊的語法,語意及其關聯,如加密解密
對談層:不同機器上的使用者建立和管理回話
傳輸層:接受數據,對數據進行分割,傳輸給網路層(TCP協定,UDP協定)
網路層:其主要功能是要完成網路中主機間「copy分組」(Packet)的傳輸。(IP協定,路由協定)
數據鏈路層:數據鏈路層是物理傳輸通道,可使用多種傳輸媒介傳輸,可建立在任何物理傳輸網上。比如光纖、雙絞線等
物理層:原始的數據可在各種物理媒體上傳輸

TCP 4 層模型
應用層:程式協定,HTTPS
傳輸層:其主要任務是向上一層提供可靠的端到端(End-to-End)服務,確保「報文」無差錯、有序、不zhidao丟失、無重複地傳輸。它向高層遮蔽了下層數據通訊的細節,是計算機通訊體系結構中最關鍵的一層。
網路層:其主要功能是要完成網路中主機間「copy分組」(Packet)的傳輸。
數據鏈路層:數據鏈路層是物理傳輸通道,可使用多種傳輸媒介傳輸,可建立在任何物理傳輸網上。比如光纖、雙絞線等

http伺服器和用戶端怎麼連線的
HTTP協定本身是無狀態的,

什麼是對談技術,cookie,session的區別

1,session 在伺服器端,cookie 在用戶端(瀏覽器)
2、session 的執行依賴 session id,而 session id 是存在 cookie 中的,也就是說,如果瀏覽器禁用了 cookie ,同時 session 也會失效,儲存Session時,鍵與Cookie中的sessionid相同,值是開發人員設定的鍵值對資訊,進行了base64編碼,過期時間由開發人員設定
3、cookie安全性比session差

裝飾器
本質是python函數,讓其他函數在不做任何變動的情況下增加額外功能,裝飾器的返回值也是一個函數物件,應用於有切面需求的場景,插入日誌,效能測試,事務處理,快取,許可權校驗
有了裝飾器就可以抽離出大量與函數功能無關的雷同程式碼進行重用
實際上就是一個閉包,把函數當做參數返回一個替代版函數

import time

def decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func()
        end_time = time.time()
        print(end_time - start_time)

    return wrapper

@decorator 
def func():
    time.sleep(0.8)

func() # 函數呼叫

生成器和迭代器
1)迭代器是一個更抽象的概念,任何物件,如果它的類有next方法和iter方法返回自己本身。對於string、list、dict、tuple等這類容器物件,使用for回圈遍歷是很方便的。在後台for語句對容器物件呼叫iter()函數,iter()是python的內建函數。iter()會返回一個定義了next()方法的迭代器物件,它在容器中逐個存取容器內元素,next()也是python的內建函數。在沒有後續元素時,next()會拋出一個StopIteration異常
實現了迭代協定的物件Python裡沒有介面協定的關鍵字但是Python裡的魔術方法就代表一種協定如果一個物件裡有__next__()和__iter__()方法,那這個物件就是迭代器。如果需要產生一個序列但是因爲序列太大,你又不希望佔用太多的記憶體。伺服器端沒辦法承受這麼大的伺服器開銷.在沒有後續元素時,next()會拋出一個StopIteration異常
省空間,但是在運算的時候耗時間的設定
(2)生成器(Generator)是建立迭代器的簡單而強大的工具。它們寫起來就像是正規的函數,只是在需要返回數據的時候使用yield語句。每次next()被呼叫時,生成器會返回它脫離的位置(它記憶語句最後一次執行的位置和所有的數據值)
區別:生成器能做到迭代器能做的所有事,而且因爲自動建立了__iter__()和next()方法,生成器顯得特別簡潔,而且生成器也是高效的,使用生成器表達式取代列表解析可以同時節省記憶體。除了建立和儲存程式狀態的自動方法,當發生器終結時,還會自動拋出StopIteration異常

m, n,k = 1,100,2
gen = (num for num in range(m,n+1,k))
for i in gen:
    print(i)
    
def foo(m,n,k):
    yield from range(m,n+1,k)
    
生成器
def fib(num):
    a, b = 0, 1
    for _ in range(num):
        a, b = b, a + b
    yield a

yield用法

簡單來說就是一個生成器,它記住上次返回時在函數體中的位置

兩個字典,怎麼合併成一個字典
使用**

def Merge(dict1, dict2): 
    res = {**dict1, **dict2} 
    return res 
      
# 兩個字典
dict1 = {'a': 10, 'b': 8} 
dict2 = {'d': 6, 'c': 4} 
dict3 = Merge(dict1, dict2) 
print(dict3)

合併時,兩個鍵一樣的數據會出現什麼情況?

​ 兩個字典如果不考慮鍵相同則相加的話,可以使用d1.update(d2)可以很方便合併,但這樣的後面的字典到中的值會覆蓋字典d1中的值