包含python函數(shù)面試題的詞條

Python面試題,線程與進(jìn)程的區(qū)別,Python中如何創(chuàng)建多線程?

進(jìn)程和線程

創(chuàng)新互聯(lián)是專業(yè)的東光網(wǎng)站建設(shè)公司,東光接單;提供成都網(wǎng)站建設(shè)、網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行東光網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊,希望更多企業(yè)前來合作!

這兩個概念屬于操作系統(tǒng),我們經(jīng)常聽說,但是可能很少有人會細(xì)究它們的含義。對于工程師而言,兩者的定義和區(qū)別還是很有必要了解清楚的。

首先說進(jìn)程,進(jìn)程可以看成是 CPU執(zhí)行的具體的任務(wù) 。在操作系統(tǒng)當(dāng)中,由于CPU的運行速度非常快,要比計算機(jī)當(dāng)中的其他設(shè)備要快得多。比如內(nèi)存、磁盤等等,所以如果CPU一次只執(zhí)行一個任務(wù),那么會導(dǎo)致CPU大量時間在等待這些設(shè)備,這樣操作效率很低。為了提升計算機(jī)的運行效率,把機(jī)器的技能盡可能壓榨出來,CPU是輪詢工作的。也就是說 它一次只執(zhí)行一個任務(wù),執(zhí)行一小段碎片時間之后立即切換 ,去執(zhí)行其他任務(wù)。

所以在早期的單核機(jī)器的時候,看起來電腦也是并發(fā)工作的。我們可以一邊聽歌一邊上網(wǎng),也不會覺得卡頓。但實際上,這是CPU輪詢的結(jié)果。在這個例子當(dāng)中,聽歌的軟件和上網(wǎng)的軟件對于CPU而言都是 獨立的進(jìn)程 。我們可以把進(jìn)程簡單地理解成運行的應(yīng)用,比如在安卓手機(jī)里面,一個app啟動的時候就會對應(yīng)系統(tǒng)中的一個進(jìn)程。當(dāng)然這種說法不完全準(zhǔn)確, 一個應(yīng)用也是可以啟動多個進(jìn)程的 。

進(jìn)程是對應(yīng)CPU而言的,線程則更多針對的是程序。即使是CPU在執(zhí)行當(dāng)前進(jìn)程的時候,程序運行的任務(wù)其實也是有分工的。舉個例子,比如聽歌軟件當(dāng)中,我們需要顯示歌詞的字幕,需要播放聲音,需要監(jiān)聽用戶的行為,比如是否發(fā)生了切歌、調(diào)節(jié)音量等等。所以,我們需要 進(jìn)一步拆分CPU的工作 ,讓它在執(zhí)行當(dāng)前進(jìn)程的時候,繼續(xù)通過輪詢的方式來同時做多件事情。

進(jìn)程中的任務(wù)就是線程,所以從這點上來說, 進(jìn)程和線程是包含關(guān)系 。一個進(jìn)程當(dāng)中可以包含多個線程,對于CPU而言,不能直接執(zhí)行線程,一個線程一定屬于一個進(jìn)程。所以我們知道,CPU進(jìn)程切換切換的是執(zhí)行的應(yīng)用程序或者是軟件,而進(jìn)程內(nèi)部的線程切換,切換的是軟件當(dāng)中具體的執(zhí)行任務(wù)。

關(guān)于進(jìn)程和線程有一個經(jīng)典的模型可以說明它們之間的關(guān)系,假設(shè)CPU是一家工廠,工廠當(dāng)中有多個車間。不同的車間對應(yīng)不同的生產(chǎn)任務(wù),有的車間生產(chǎn)汽車輪胎,有的車間生產(chǎn)汽車骨架。但是工廠的電力是有限的,同時只能滿足一個廠房的使用。

為了讓大家的進(jìn)度協(xié)調(diào),所以工廠需要輪流提供各個車間的供電。 這里的車間對應(yīng)的就是進(jìn)程 。

一個車間雖然只生產(chǎn)一種產(chǎn)品,但是其中的工序卻不止一個。一個車間可能會有好幾條流水線,具體的生產(chǎn)任務(wù)其實是流水線完成的,每一條流水線對應(yīng)一個具體執(zhí)行的任務(wù)。但是同樣的, 車間同一時刻也只能執(zhí)行一條流水線 ,所以我們需要車間在這些流水線之間切換供電,讓各個流水線生產(chǎn)進(jìn)度統(tǒng)一。

這里車間里的 流水線自然對應(yīng)的就是線程的概念 ,這個模型很好地詮釋了CPU、進(jìn)程和線程之間的關(guān)系。實際的原理也的確如此,不過CPU中的情況要比現(xiàn)實中的車間復(fù)雜得多。因為對于進(jìn)程和CPU來說,它們面臨的局面都是實時變化的。車間當(dāng)中的流水線是x個,下一刻可能就成了y個。

了解完了線程和進(jìn)程的概念之后,對于理解電腦的配置也有幫助。比如我們買電腦,經(jīng)常會碰到一個術(shù)語,就是這個電腦的CPU是某某核某某線程的。比如我當(dāng)年買的第一臺筆記本是4核8線程的,這其實是在說這臺電腦的CPU有 4個計算核心 ,但是使用了超線程技術(shù),使得可以把一個物理核心模擬成兩個邏輯核心。相當(dāng)于我們可以用4個核心同時執(zhí)行8個線程,相當(dāng)于8個核心同時執(zhí)行,但其實有4個核心是模擬出來的虛擬核心。

有一個問題是 為什么是4核8線程而不是4核8進(jìn)程呢 ?因為CPU并不會直接執(zhí)行進(jìn)程,而是執(zhí)行的是進(jìn)程當(dāng)中的某一個線程。就好像車間并不能直接生產(chǎn)零件,只有流水線才能生產(chǎn)零件。車間負(fù)責(zé)的更多是資源的調(diào)配,所以教科書里有一句非常經(jīng)典的話來詮釋: 進(jìn)程是資源分配的最小單元,線程是CPU調(diào)度的最小單元 。

啟動線程

Python當(dāng)中為我們提供了完善的threading庫,通過它,我們可以非常方便地創(chuàng)建線程來執(zhí)行多線程。

首先,我們引入threading中的Thread,這是一個線程的類,我們可以通過創(chuàng)建一個線程的實例來執(zhí)行多線程。

from threading import Thread t = Thread(target=func, name='therad', args=(x, y)) t.start()

簡單解釋一下它的用法,我們傳入了三個參數(shù),分別是 target,name和args ,從名字上我們就可以猜測出它們的含義。首先是target,它傳入的是一個方法,也就是我們希望多線程執(zhí)行的方法。name是我們?yōu)檫@個新創(chuàng)建的線程起的名字,這個參數(shù)可以省略,如果省略的話,系統(tǒng)會為它起一個系統(tǒng)名。當(dāng)我們執(zhí)行Python的時候啟動的線程名叫MainThread,通過線程的名字我們可以做區(qū)分。args是會傳遞給target這個函數(shù)的參數(shù)。

我們來舉個經(jīng)典的例子:

import time, threading # 新線程執(zhí)行的代碼: def loop(n): print('thread %s is running...' % threading.current_thread().name) for i in range(n): print('thread %s %s' % (threading.current_thread().name, i)) time.sleep(5) print('thread %s ended.' % threading.current_thread().name) print('thread %s is running...' % threading.current_thread().name) t = threading.Thread(target=loop, name='LoopThread', args=(10, )) t.start() print('thread %s ended.' % threading.current_thread().name)

我們創(chuàng)建了一個非常簡單的loop函數(shù),用來執(zhí)行一個循環(huán)來打印數(shù)字,我們每次打印一個數(shù)字之后這個線程會睡眠5秒鐘,所以我們看到的結(jié)果應(yīng)該是每過5秒鐘屏幕上多出一行數(shù)字。

我們在Jupyter里執(zhí)行一下:

表面上看這個結(jié)果沒毛病,但是其實有一個問題,什么問題呢? 輸出的順序不太對 ,為什么我們在打印了第一個數(shù)字0之后,主線程就結(jié)束了呢?另外一個問題是,既然主線程已經(jīng)結(jié)束了, 為什么Python進(jìn)程沒有結(jié)束 , 還在向外打印結(jié)果呢?

因為線程之間是獨立的,對于主線程而言,它在執(zhí)行了t.start()之后,并 不會停留,而是會一直往下執(zhí)行一直到結(jié)束 。如果我們不希望主線程在這個時候結(jié)束,而是阻塞等待子線程運行結(jié)束之后再繼續(xù)運行,我們可以在代碼當(dāng)中加上t.join()這一行來實現(xiàn)這點。

t.start() t.join() print('thread %s ended.' % threading.current_thread().name)

join操作可以讓主線程在join處掛起等待,直到子線程執(zhí)行結(jié)束之后,再繼續(xù)往下執(zhí)行。我們加上了join之后的運行結(jié)果是這樣的:

這個就是我們預(yù)期的樣子了,等待子線程執(zhí)行結(jié)束之后再繼續(xù)。

我們再來看第二個問題,為什么主線程結(jié)束的時候,子線程還在繼續(xù)運行,Python進(jìn)程沒有退出呢?這是因為默認(rèn)情況下我們創(chuàng)建的都是用戶級線程,對于進(jìn)程而言, 會等待所有用戶級線程執(zhí)行結(jié)束之后才退出 。這里就有了一個問題,那假如我們創(chuàng)建了一個線程嘗試從一個接口當(dāng)中獲取數(shù)據(jù),由于接口一直沒有返回,當(dāng)前進(jìn)程豈不是會永遠(yuǎn)等待下去?

這顯然是不合理的,所以為了解決這個問題,我們可以把創(chuàng)建出來的線程設(shè)置成 守護(hù)線程 。

守護(hù)線程

守護(hù)線程即daemon線程,它的英文直譯其實是后臺駐留程序,所以我們也可以理解成 后臺線程 ,這樣更方便理解。daemon線程和用戶線程級別不同,進(jìn)程不會主動等待daemon線程的執(zhí)行, 當(dāng)所有用戶級線程執(zhí)行結(jié)束之后即會退出。進(jìn)程退出時會kill掉所有守護(hù)線程 。

我們傳入daemon=True參數(shù)來將創(chuàng)建出來的線程設(shè)置成后臺線程:

t = threading.Thread(target=loop, name='LoopThread', args=(10, ), daemon=True)

這樣我們再執(zhí)行看到的結(jié)果就是這樣了:

這里有一點需要注意,如果你 在jupyter當(dāng)中運行是看不到這樣的結(jié)果的 。因為jupyter自身是一個進(jìn)程,對于jupyter當(dāng)中的cell而言,它一直是有用戶級線程存活的,所以進(jìn)程不會退出。所以想要看到這樣的效果,只能通過命令行執(zhí)行Python文件。

如果我們想要等待這個子線程結(jié)束,就必須通過join方法。另外,為了預(yù)防子線程鎖死一直無法退出的情況, 我們還可以 在joih當(dāng)中設(shè)置timeout ,即最長等待時間,當(dāng)?shù)却龝r間到達(dá)之后,將不再等待。

比如我在join當(dāng)中設(shè)置的timeout等于5時,屏幕上就只會輸出5個數(shù)字。

另外,如果沒有設(shè)置成后臺線程的話,設(shè)置timeout雖然也有用,但是 進(jìn)程仍然會等待所有子線程結(jié)束 。所以屏幕上的輸出結(jié)果會是這樣的:

雖然主線程繼續(xù)往下執(zhí)行并且結(jié)束了,但是子線程仍然一直運行,直到子線程也運行結(jié)束。

關(guān)于join設(shè)置timeout這里有一個坑,如果我們只有一個線程要等待還好,如果有多個線程,我們用一個循環(huán)將它們設(shè)置等待的話。那么 主線程一共會等待N * timeout的時間 ,這里的N是線程的數(shù)量。因為每個線程計算是否超時的開始時間是上一個線程超時結(jié)束的時間,它會等待所有線程都超時,才會一起終止它們。

比如我這樣創(chuàng)建3個線程:

ths = [] for i in range(3): t = threading.Thread(target=loop, name='LoopThread' + str(i), args=(10, ), daemon=True) ths.append(t) for t in ths: t.start() for t in ths: t.join(2)

最后屏幕上輸出的結(jié)果是這樣的:

所有線程都存活了6秒。

總結(jié)

在今天的文章當(dāng)中,我們一起簡單了解了 操作系統(tǒng)當(dāng)中線程和進(jìn)程的概念 ,以及Python當(dāng)中如何創(chuàng)建一個線程,以及關(guān)于創(chuàng)建線程之后的相關(guān)使用。

多線程在許多語言當(dāng)中都是至關(guān)重要的,許多場景下必定會使用到多線程。比如 web后端,比如爬蟲,再比如游戲開發(fā) 以及其他所有需要涉及開發(fā)ui界面的領(lǐng)域。因為凡是涉及到ui,必然會需要一個線程單獨渲染頁面,另外的線程負(fù)責(zé)準(zhǔn)備數(shù)據(jù)和執(zhí)行邏輯。因此,多線程是專業(yè)程序員繞不開的一個話題,也是一定要掌握的內(nèi)容之一。

2022年P(guān)ython技術(shù)類面試題總結(jié)(面試題+答案解析)

這是一位有著五年 Python 經(jīng)驗的好友最近對 Python 崗位面試后的一篇經(jīng)驗總結(jié),從 Python 就業(yè)方向到 Python 面試題。

Python 就業(yè)方向 :

下面是 Python 面試知識點,總結(jié)了華為、阿里巴巴等互聯(lián)網(wǎng)公司 Python 常問面試題。每道題都提供參考答案,希望能夠幫助你在求職面試中脫穎而出,找到一份高薪工作。

這些面試題分為 Python 基礎(chǔ)和 Python高級,內(nèi)容包含: 基礎(chǔ)語法、文件操作、模塊與包、數(shù)據(jù)類型、元類、內(nèi)存管理與垃圾回收機(jī)制以及 Python 函數(shù) 等知識點。

(一) Python 基礎(chǔ)語法

(二) 文件操作

(三) 模塊與包

(四) 數(shù)據(jù)類型

(五)企業(yè)面試題

(一) 元類

(二)內(nèi)存管理與垃圾回收機(jī)制

(三)函數(shù)

(四) 面向?qū)ο?/p>

由于篇幅有限,這份 Python 面試寶典已經(jīng)被整理成了PDF文檔,有需要 Python 面試寶典全套完整文檔(面試題+答案解析)的可以 免費領(lǐng)取!

這個python題目怎么寫?

無意間,看到這么一道Python面試題:以下代碼將輸出什么?

def testFun:

temp = [lambda x : i*x for i in range(4)]

return temp

for everyLambda in testFun:

print (everyLambda(2))

腦中默默一想,這還用說么,肯定是:

2

4

6

最后一看答案,竟然是:

6

6

6

6

于是帶著懷疑的心態(tài)(其實是不服輸,不認(rèn)錯),打開編輯器,快速一敲,果然是:

懷疑了人生半天,本來還想黑,WTF Python…然后才想通是自己太生疏......

最后發(fā)現(xiàn)原因竟是:Python 的閉包的后期綁定導(dǎo)致的 late binding。

這意味著在閉包中的變量是在內(nèi)部函數(shù)被調(diào)用的時候被查找,所以當(dāng)任何testFun 返回的函數(shù)被調(diào)用,i 的值是在它被調(diào)用時的周圍作用域中查找。

也就是說無論哪個返回的函數(shù)被調(diào)用,for 循環(huán)都已經(jīng)完成了,i 最后的值是 3,因此,每個返回的函數(shù) testFun 的值都是 3。

因此一個等于 2 的值被傳遞進(jìn)以上代碼,它們將返回一個值 6 (比如:3 x 2)。

究竟如何才能實現(xiàn)出這樣的結(jié)果呢?

2

4

6

想了想,若能立即綁定參數(shù),或者直接不用閉包總該行吧,用另一種方式避免 i 的改寫。

回憶了之前所學(xué)知識,最后醞釀出了四種解決方案。

第一種:創(chuàng)建一個閉包,通過使用默認(rèn)參數(shù)立即綁定它的參數(shù)

def testFun:

temp = [lambda x, i=i: i * x for i in range(4)]

return temp

for everyLambda in testFun:

print(everyLambda(2))

第二種:使用functools.partial 函數(shù),把函數(shù)的某些參數(shù)(不管有沒有默認(rèn)值)給固定住(也就是相當(dāng)于設(shè)置默認(rèn)值)

from functools import partial

from operator import mul

def testFun:

return [partial(mul, i) for i in range(4)]

for everyLambda in testFun:

print(everyLambda(2))

第三種:優(yōu)雅的寫法,直接用生成器

def testFun:

return (lambda x, i=i: i * x for i in range(4))

for everyLambda in testFun:

print(everyLambda(2))

第四種:利用yield的惰性求值的思想

def testFun:

for i in range(4):

yield lambda x: i * x

for everyLambda in testFun:

print(everyLambda(2))

最終運行結(jié)果:

有了解決方案后,又陷入了懷疑自己,這個題目究竟是考察的是什么?是在考面試者閉包相關(guān)知識以及Python 的閉包的后期綁定問題么?

若將題目改成:以下代碼輸出的結(jié)果是(0,2,4,6)么?如果不是,你將會怎么做,讓它變成(0,2,4,6)?這樣會不會更有意思點呢?歡迎大家出妙招,看究竟有多少招?(哈哈哈?。。。?/p>

python面試題總結(jié)1-內(nèi)存管理機(jī)制

(1).引用計數(shù)

(2). 垃圾回收

(3). 內(nèi)存池機(jī)制

在python中每創(chuàng)建一個對象,對應(yīng)的會有一個引用計數(shù),當(dāng)發(fā)生賦值操作如a=b,對應(yīng)的b的引用計數(shù)會自動加1,當(dāng)引用的對象被清除或者函數(shù)結(jié)束時,引用計數(shù)會自動減1。

在python中使用引用計數(shù),標(biāo)記清楚,分代回收三種方式進(jìn)行垃圾回收。

其中,引用計數(shù)當(dāng)對象的引用計數(shù)歸0時,對象會自動被清除。標(biāo)記清除機(jī)制是首先遍歷所有對象,如果對象可達(dá),就說明有變量引用它,則標(biāo)記其為可達(dá)的。如果不可達(dá),則對其進(jìn)行清除。分代回收是當(dāng)對象創(chuàng)建時被標(biāo)記為第0代,經(jīng)過一次垃圾回收之后,余下的對象被標(biāo)記為第1代,最高為第2代。其原理是,對象的生存期越長,月可能不是垃越。

ython語言雖然提供了對內(nèi)存的垃圾收集機(jī)制,但實際上它將不用的內(nèi)存放到內(nèi)存池而不是返回給操作系統(tǒng),所以就有了以下:

1 Pymalloc機(jī)制;這個主要是為了加速Python的執(zhí)行效率,Python引入了一個內(nèi)存池機(jī)制,用于管理,為了對小塊內(nèi)存的申請和釋放。

2 Python中所有小于256個字節(jié)的對象都是依靠pymalloc分配器來實現(xiàn)的,而稍大的對象用的則是系統(tǒng)的malloc。

3 對于Python對象,比如整數(shù)、浮點數(shù)和List這些,都有自己獨立的內(nèi)存池,對象間并不共享他們的內(nèi)存池。換句話說就是,假設(shè)你分配并且釋放了大量的整數(shù),那么用于緩存這些整數(shù)的內(nèi)存就不能再分配給浮點數(shù)。

分享標(biāo)題:包含python函數(shù)面試題的詞條
網(wǎng)站地址:http://muchs.cn/article28/doedpcp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)、網(wǎng)站建設(shè)、搜索引擎優(yōu)化、靜態(tài)網(wǎng)站、小程序開發(fā)、企業(yè)建站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管