python 語言歷史誕生初始以及所在領(lǐng)域

認(rèn)識(shí)python(了解)

1. Python發(fā)展歷史

起源

Python的作者,Guido von Rossum

荷蘭人。1982年,Guido從阿姆斯特丹大學(xué)獲得了數(shù)學(xué)和計(jì)算機(jī)碩士學(xué)位。然而,盡管他算得上是一位數(shù)學(xué)家,但他更加享受計(jì)算機(jī)帶來的樂趣。用他的話說,盡管擁有數(shù)學(xué)和計(jì)算機(jī)雙料資質(zhì),他總趨向于做計(jì)算機(jī)相關(guān)的工作,并熱衷于做任何和編程相關(guān)的活兒。

在那個(gè)時(shí)候,Guido接觸并使用過諸如Pascal、C、Fortran等語言。這些語言的基本設(shè)計(jì)原則是讓機(jī)器能更快運(yùn)行。在80年代,雖然IBM和蘋果已經(jīng)掀起了個(gè)人電腦浪潮,但這些個(gè)人電腦的配置很低。比如早期的Macintosh,只有8MHz的CPU主頻和128KB的RAM,一個(gè)大的數(shù)組就能占滿內(nèi)存。所有的編譯器的核心是做優(yōu)化,以便讓程序能夠運(yùn)行。為了增進(jìn)效率,語言也迫使程序員像計(jì)算機(jī)一樣思考,以便能寫出更符合機(jī)器口味的程序。在那個(gè)時(shí)代,程序員恨不得用手榨取計(jì)算機(jī)每一寸的能力。有人甚至認(rèn)為C語言的指針是在浪費(fèi)內(nèi)存。至于動(dòng)態(tài)類型,內(nèi)存自動(dòng)管理,面向?qū)ο蟆?/b>

別想了,那會(huì)讓你的電腦陷入癱瘓。

這種編程方式讓Guido感到苦惱。Guido知道如何用C語言寫出一個(gè)功能,但整個(gè)編寫過程需要耗費(fèi)大量的時(shí)間,即使他已經(jīng)準(zhǔn)確的知道了如何實(shí)現(xiàn)。他的另一個(gè)選擇是shell。Bourne

Shell作為UNIX系統(tǒng)的解釋器已經(jīng)長期存在。UNIX的管理員們常常用shell去寫一些簡單的腳本,以進(jìn)行一些系統(tǒng)維護(hù)的工作,比如定期備份、文件系統(tǒng)管理等等。shell可以像膠水一樣,將UNIX下的許多功能連接在一起。許多C語言下上百行的程序,在shell下只用幾行就可以完成。然而,shell的本質(zhì)是調(diào)用命令。它并不是一個(gè)真正的語言。比如說,shell沒有數(shù)值型的數(shù)據(jù)類型,加法運(yùn)算都很復(fù)雜。總之,shell不能全面的調(diào)動(dòng)計(jì)算機(jī)的功能。

Guido希望有一種語言,這種語言能夠像C語言那樣,能夠全面調(diào)用計(jì)算機(jī)的功能接口,又可以像shell那樣,可以輕松的編程。ABC語言讓Guido看到希望。ABC是由荷蘭的數(shù)學(xué)和計(jì)算機(jī)研究所開發(fā)的。Guido在該研究所工作,并參與到ABC語言的開發(fā)。ABC語言以教學(xué)為目的。與當(dāng)時(shí)的大部分語言不同,ABC語言的目標(biāo)是“讓用戶感覺更好”。ABC語言希望讓語言變得容易閱讀,容易使用,容易記憶,容易學(xué)習(xí),并以此來激發(fā)人們學(xué)習(xí)編程的興趣。比如下面是一段來自Wikipedia的ABC程序,這個(gè)程序用于統(tǒng)計(jì)文本中出現(xiàn)的詞的總數(shù):

HOW TO RETURN words document:? ? ? PUT {} IN collection? ? ? FOR line IN document:? ? ? ? FOR word IN split line:? ? ? ? ? ? IF wordnot.incollection:? ? ? ? ? ? ? INSERT word IN collection? ? ? RETURN collection

HOW TO用于定義一個(gè)函數(shù)。一個(gè)Python程序員應(yīng)該很容易理解這段程序。ABC語言使用冒號和縮進(jìn)來表示程序塊。行

尾沒有分號。for和if結(jié)構(gòu)中也沒有括號() 。賦值采用的是PUT,而不是更常見的等號。這些改動(dòng)讓ABC程序讀起來像一段文字。

盡管已經(jīng)具備了良好的可讀性和易用性,ABC語言最終沒有流行起來。在當(dāng)時(shí),ABC語言編譯器需要比較高配置的電腦才能運(yùn)行。而這些電腦的使用者通常精通計(jì)算機(jī),他們更多考慮程序的效率,而非它的學(xué)習(xí)難度。除了硬件上的困難外,ABC語言的設(shè)計(jì)也存在一些致命的問題:

可拓展性差。ABC語言不是模塊化語言。如果想在ABC語言中增加功能,比如對圖形化的支持,就必須改動(dòng)很多地方。

不能直接進(jìn)行IO。ABC語言不能直接操作文件系統(tǒng)。盡管你可以通過諸如文本流的方式導(dǎo)入數(shù)據(jù),但ABC無法直接讀寫文

件。輸入輸出的困難對于計(jì)算機(jī)語言來說是致命的。你能想像一個(gè)打不開車門的跑車么?

過度革新。ABC用自然語言的方式來表達(dá)程序的意義,比如上面程序中的HOW TO 。然而對于程序員來說,他們更習(xí)慣

用function或者define來定義一個(gè)函數(shù)。同樣,程序員更習(xí)慣用等號來分配變量。盡管ABC語言很特別,但學(xué)習(xí)難度 也很大。

傳播困難。ABC編譯器很大,必須被保存在磁帶上。當(dāng)時(shí)Guido在訪問的時(shí)候,就必須有一個(gè)大磁帶來給別人安裝ABC編 譯器。

這樣,ABC語言就很難快速傳播。

1989年,為了打發(fā)圣誕節(jié)假期,Guido開始寫Python語言的編譯器。Python這個(gè)名字,來自Guido所摯愛的電視劇Monty

Python's Flying

Circus。他希望這個(gè)新的叫做Python的語言,能符合他的理想:創(chuàng)造一種C和shell之間,功能全面,易學(xué)易用,可拓展的語言。Guido作為一個(gè)語言設(shè)計(jì)愛好者,已經(jīng)有過設(shè)計(jì)語言的嘗試。這一次,也不過是一次純粹的hacking行為。

一門語言的誕生

1991年,第一個(gè)Python編譯器誕生。它是用C語言實(shí)現(xiàn)的,并能夠調(diào)用C語言的庫文件。從一出生,Python已經(jīng)具有了

:類,函數(shù),異常處理,包含表和詞典在內(nèi)的核心數(shù)據(jù)類型,以及模塊為基礎(chǔ)的拓展系統(tǒng)。

Python語法很多來自C,但又受到ABC語言的強(qiáng)烈影響。來自ABC語言的一些規(guī)定直到今天還富有爭議,比如強(qiáng)制縮進(jìn)。

但這些語法規(guī)定讓Python容易讀。另一方面,Python聰明的選擇服從一些慣例,特別是C語言的慣例,比如回歸等號賦值。Guido認(rèn)為,如果“常識(shí)”上確立的東西,沒有必要過度糾結(jié)。

Python從一開始就特別在意可拓展性。Python可以在多個(gè)層次上拓展。從高層上,你可以直接引入.

py文件。在底層,你可以引用C語言的庫。Python程序員可以快速的使用Python寫.

py文件作為拓展模塊。但當(dāng)性能是考慮的重要因素時(shí),Python程序員可以深入底層,寫C程序,編譯為.so文件引入到Python中使用。Python就好像是使用鋼構(gòu)建房一樣,先規(guī)定好大的框架。而程序員可以在此框架下相當(dāng)自由的拓展或更

改。

最初的Python完全由Guido本人開發(fā)。Python得到Guido同事的歡迎。他們迅速的反饋使用意見,并參與到Python的改進(jìn)。Guido和一些同事構(gòu)成Python的核心團(tuán)隊(duì)。他們將自己大部分的業(yè)余時(shí)間用于hack

Python。隨后,Python拓

展到研究所之外。Python將許多機(jī)器層面上的細(xì)節(jié)隱藏,交給編譯器處理,并凸顯出邏輯層面的編程思考。Python程

序員可以花更多的時(shí)間用于思考程序的邏輯,而不是具體的實(shí)現(xiàn)細(xì)節(jié)。這一特征吸引了廣大的程序員。Python開始流行。


人生苦短,我用python

時(shí)勢造英雄

我們不得不暫停我們的Python時(shí)間,轉(zhuǎn)而看一看瞬息萬變的計(jì)算機(jī)行業(yè)。1990年代初,個(gè)人計(jì)算機(jī)開始進(jìn)入普通家庭。Intel發(fā)布了486處理器,windows發(fā)布window

3.0開始的一系列視窗系統(tǒng)。計(jì)算機(jī)的性能大大提高。程序員開始關(guān)注計(jì)算機(jī)的易用性,比如圖形化界面。

Windows 3.0

由于計(jì)算機(jī)性能的提高,軟件的世界也開始隨之改變。硬件足以滿足許多個(gè)人電腦的需要。硬件廠商甚至渴望高需求軟

件的出現(xiàn),以帶動(dòng)硬件的更新?lián)Q代。C++和Java相繼流行。C++和Java提供了面向?qū)ο蟮木幊谭妒?,以及豐富的對象庫。在犧牲了一定的性能的代價(jià)下,C++和Java大大提高了程序的產(chǎn)量。語言的易用性被提到一個(gè)新的高度。我們還記得

,ABC失敗的一個(gè)重要原因是硬件的性能限制。從這方面說,Python要比ABC幸運(yùn)許多。

另一個(gè)悄然發(fā)生的改變是Internet。1990年代還是個(gè)人電腦的時(shí)代,windows和Intel挾PC以令天下,盛極一時(shí)。盡管Internet為主體的信息革命尚未到來,但許多程序員以及資深計(jì)算機(jī)用戶已經(jīng)在頻繁使用Internet進(jìn)行交流,比如

使用email和newsgroup。Internet讓信息交流成本大大下降。一種新的軟件開發(fā)模式開始流行:開源。程序員利用

業(yè)余時(shí)間進(jìn)行軟件開發(fā),并開放源代碼。1991年,Linus在comp.os.minix新聞組上發(fā)布了Linux內(nèi)核源代碼,吸引大批hacker的加入。Linux和GNU相互合作,最終構(gòu)成了一個(gè)充滿活力的開源平臺(tái)。

硬件性能不是瓶頸,Python又容易使用,所以許多人開始轉(zhuǎn)向Python。Guido維護(hù)了一個(gè)maillist,Python用戶就通過郵件進(jìn)行交流。Python用戶來自許多領(lǐng)域,有不同的背景,對Python也有不同的需求。Python相當(dāng)?shù)拈_放,又容

易拓展,所以當(dāng)用戶不滿足于現(xiàn)有功能,很容易對Python進(jìn)行拓展或改造。隨后,這些用戶將改動(dòng)發(fā)給Guido,并由Gu

ido決定是否將新的特征加入到Python或者標(biāo)準(zhǔn)庫中。如果代碼能被納入Python自身或者標(biāo)準(zhǔn)庫,這將極大的榮譽(yù)。由于Guido至高無上的決定權(quán),他因此被稱為“終身的仁慈獨(dú)裁者”。

Python被稱為“Battery

Included”,是說它以及其標(biāo)準(zhǔn)庫的功能強(qiáng)大。這些是整個(gè)社區(qū)的貢獻(xiàn)。Python的開發(fā)者來自不同領(lǐng)域,他們將不同領(lǐng)域的優(yōu)點(diǎn)帶給Python。比如Python標(biāo)準(zhǔn)庫中的正則表達(dá)是參考Perl,而lambda,

map, filter, reduce等函數(shù)參考了Lisp。Python本身的一些功能以及大部分的標(biāo)準(zhǔn)庫來自于社區(qū)。Python的社

區(qū)不斷擴(kuò)大,進(jìn)而擁有了自己的newsgroup,網(wǎng)站,以及基金。從Python

2.0開始,Python也從maillist的開發(fā)方式,轉(zhuǎn)為完全開源的開發(fā)方式。社區(qū)氣氛已經(jīng)形成,工作被整個(gè)社區(qū)分擔(dān),Python也獲得了更加高速的發(fā)展。

到今天,Python的框架已經(jīng)確立。Python語言以對象為核心組織代碼,支持多種編程范式,采用動(dòng)態(tài)類型,自動(dòng)進(jìn)行內(nèi)存回收。Python支持解釋運(yùn)行,并能調(diào)用C庫進(jìn)行拓展。Python有強(qiáng)大的標(biāo)準(zhǔn)庫。由于標(biāo)準(zhǔn)庫的體系已經(jīng)穩(wěn)定,所以Python的生態(tài)系統(tǒng)開始拓展到第三方包。這些包,如Django、web.py、wxpython、numpy、matplotlib、PIL,將Python升級成了物種豐富的熱帶雨林。

啟示錄

Python崇尚優(yōu)美、清晰、簡單,是一個(gè)優(yōu)秀并廣泛使用的語言。Python在TIOBE排行榜中排行第八,它是Google的第三大開發(fā)語言,Dropbox的基礎(chǔ)語言,豆瓣的服務(wù)器語言。Python的發(fā)展史可以作為一個(gè)代表,帶給我許多啟示。

在Python的開發(fā)過程中,社區(qū)起到了重要的作用。Guido自認(rèn)為自己不是全能型的程序員,所以他只負(fù)責(zé)制訂框架。如果問題太復(fù)雜,他會(huì)選擇繞過去,也就是cut

the

corner。這些問題最終由社區(qū)中的其他人解決。社區(qū)中的人才是異常豐富的,就連創(chuàng)建網(wǎng)站,籌集基金這樣與開發(fā)稍遠(yuǎn)的事情,也有人樂意于處理。如今的項(xiàng)目開發(fā)越來越復(fù)雜,越來越龐大,合作以及開放的心態(tài)成為項(xiàng)目最終成功的關(guān)鍵。

Python從其他語言中學(xué)到了很多,無論是已經(jīng)進(jìn)入歷史的ABC,還是依然在使用的C和Perl,以及許多沒有列出的其他

語言??梢哉f,Python的成功代表了它所有借鑒的語言的成功。同樣,Ruby借鑒了Python,它的成功也代表了Python某些方面的成功。每個(gè)語言都是混合體,都有它優(yōu)秀的地方,但也有各種各樣的缺陷。同時(shí),一個(gè)語言“好與不好”的評

判,往往受制于平臺(tái)、硬件、時(shí)代等等外部原因。程序員經(jīng)歷過許多語言之爭。其實(shí),以開放的心態(tài)來接受各個(gè)語言,說不定哪一天,程序員也可以如Guido那樣,混合出自己的語言。


Numpy的介紹


NumPy(Numerical Python) 是 Python 語言的一個(gè)擴(kuò)展程序庫,支持大量的維度數(shù)組與矩陣運(yùn)算,此外也針對數(shù)組運(yùn)算提供大量的數(shù)學(xué)函數(shù)庫。

NumPy 的前身 Numeric 最早是由 Jim Hugunin 與其它協(xié)作者共同開發(fā),2005 年,Travis Oliphant 在

Numeric 中結(jié)合了另一個(gè)同性質(zhì)的程序庫 Numarray 的特色,并加入了其它擴(kuò)展而開發(fā)了 NumPy。NumPy

為開放源代碼并且由許多協(xié)作者共同維護(hù)開發(fā)。

NumPy 是一個(gè)運(yùn)行速度非??斓臄?shù)學(xué)庫,主要用于數(shù)組計(jì)算,包含:

一個(gè)強(qiáng)大的N維數(shù)組對象 ndarray

廣播功能函數(shù)

整合 C/C++/Fortran 代碼的工具

線性代數(shù)、傅里葉變換、隨機(jī)數(shù)生成等功能


關(guān)鍵點(diǎn)常識(shí)


Python的發(fā)音與拼寫

Python的意思是蟒蛇,源于作者喜歡的一部電視劇 (C呢?)

Python的作者是Guido van Rossum(龜叔)

Python是龜叔在1989年圣誕節(jié)期間,為了打發(fā)無聊的圣誕節(jié)而用C編寫的一個(gè)編程語言

Python正式誕生于1991年

Python的解釋器如今有多個(gè)語言實(shí)現(xiàn),我們常用的是CPython(官方版本的C語言實(shí)現(xiàn)),其他還有Jython(可以運(yùn)行在Java平臺(tái))、IronPython(可以運(yùn)行在.NET和Mono平臺(tái))、PyPy(Python實(shí)現(xiàn)的,支持JIT即時(shí)編譯)

Python目前有兩個(gè)版本,Python2和Python3,最新版分別為2.7.12和3.5.2,現(xiàn)階段大部分公司用的是Python2

Life is shot, you need Python. 人生苦短,我用Python。

2017年1月份 編程語言流行排行榜


2. Python優(yōu)缺點(diǎn)

優(yōu)點(diǎn)


簡單————Python是一種代表簡單主義思想的語言。閱讀一個(gè)良好的Python程序就感覺像是在讀英語一樣,盡管這個(gè)英語的要求非常嚴(yán)格!Python的這種偽代碼本質(zhì)是它最大的優(yōu)點(diǎn)之一。它使你能夠?qū)W⒂诮鉀Q問題而不是去搞明白語言本身。

易學(xué)————就如同你即將看到的一樣,Python極其容易上手。前面已經(jīng)提到了,Python有極其簡單的語法。

免費(fèi)、開源————Python是FLOSS(自由/開放源碼軟件)之一。簡單地說,你可以自由地發(fā)布這個(gè)軟件的拷貝、閱讀它的源代碼、對它做改動(dòng)、把它的一部分用于新的自由軟件中。FLOSS是基于一個(gè)團(tuán)體分享知識(shí)的概念。這是為什么Python如此優(yōu)秀的原因之一——它是由一群希望看到一個(gè)更加優(yōu)秀的Python的人創(chuàng)造并經(jīng)常改進(jìn)著的。

高層語言————當(dāng)你用Python語言編寫程序的時(shí)候,你無需考慮諸如如何管理你的程序使用的內(nèi)存一類的底層細(xì)節(jié)。

可移植性————由于它的開源本質(zhì),Python已經(jīng)被移植在許多平臺(tái)上(經(jīng)過改動(dòng)使它能夠工作在不同平臺(tái)上)。如果你小心地避免使用依賴于系統(tǒng)的特性,那么你的所有Python程序無需修改就可以在下述任何平臺(tái)上面運(yùn)行。這些平臺(tái)包括Linux、Windows、FreeBSD、Macintosh、Solaris、OS/2、Amiga、AROS、AS/400、BeOS、OS/390、z/OS、Palm

OS、QNX、VMS、Psion、Acom RISC OS、VxWorks、PlayStation、Sharp Zaurus、Windows

CE甚至還有PocketPC、Symbian以及Google基于linux開發(fā)的Android平臺(tái)!

解釋性————這一點(diǎn)需要一些解釋。一個(gè)用編譯性語言比如C或C++寫的程序可以從源文件(即C或C++語言)轉(zhuǎn)換到一個(gè)你的計(jì)算機(jī)使用的語言(二進(jìn)制代碼,即0和1)。這個(gè)過程通過編譯器和不同的標(biāo)記、選項(xiàng)完成。當(dāng)你運(yùn)行你的程序的時(shí)候,連接/轉(zhuǎn)載器軟件把你的程序從硬盤復(fù)制到內(nèi)存中并且運(yùn)行。而Python語言寫的程序不需要編譯成二進(jìn)制代碼。你可以直接從源代碼運(yùn)行程序。在計(jì)算機(jī)內(nèi)部,Python解釋器把源代碼轉(zhuǎn)換成稱為字節(jié)碼的中間形式,然后再把它翻譯成計(jì)算機(jī)使用的機(jī)器語言并運(yùn)行。事實(shí)上,由于你不再需要擔(dān)心如何編譯程序,如何確保連接轉(zhuǎn)載正確的庫等等,所有這一切使得使用Python更加簡單。由于你只需要把你的Python程序拷貝到另外一臺(tái)計(jì)算機(jī)上,它就可以工作了,這也使得你的Python程序更加易于移植。

面向?qū)ο蟆狿ython既支持面向過程的編程也支持面向?qū)ο蟮木幊?。在“面向過程”的語言中,程序是由過程或僅僅是可重用代碼的函數(shù)構(gòu)建起來的。在“面向?qū)ο蟆钡恼Z言中,程序是由數(shù)據(jù)和功能組合而成的對象構(gòu)建起來的。與其他主要的語言如C++和Java相比,Python以一種非常強(qiáng)大又簡單的方式實(shí)現(xiàn)面向?qū)ο缶幊獭?/b>

可擴(kuò)展性————如果你需要你的一段關(guān)鍵代碼運(yùn)行得更快或者希望某些算法不公開,你可以把你的部分程序用C或C++編寫,然后在你的Python程序中使用它們。

豐富的庫————Python標(biāo)準(zhǔn)庫確實(shí)很龐大。它可以幫助你處理各種工作,包括正則表達(dá)式、文檔生成、單元測試、線程、數(shù)據(jù)庫、網(wǎng)頁瀏覽器、CGI、FTP、電子郵件、XML、XML-RPC、HTML、WAV文件、密碼系統(tǒng)、GUI(圖形用戶界面)、Tk和其他與系統(tǒng)有關(guān)的操作。記住,只要安裝了Python,所有這些功能都是可用的。這被稱作Python的“功能齊全”理念。除了標(biāo)準(zhǔn)庫以外,還有許多其他高質(zhì)量的庫,如wxPython、Twisted和Python圖像庫等等。

規(guī)范的代碼————Python采用強(qiáng)制縮進(jìn)的方式使得代碼具有極佳的可讀性。


缺點(diǎn)


運(yùn)行速度,有速度要求的話,用C++改寫關(guān)鍵部分吧。

國內(nèi)市場較?。▏鴥?nèi)以python來做主要開發(fā)的,目前只有一些web2.0公司)。但時(shí)間推移,目前很多國內(nèi)軟件公司,尤其是游戲公司,也開始規(guī)模使用他。

中文資料匱乏(好的python中文資料屈指可數(shù))。托社區(qū)的福,有幾本優(yōu)秀的教材已經(jīng)被翻譯了,但入門級教材多,高級內(nèi)容還是只能看英語版。

構(gòu)架選擇太多(沒有像C#這樣的官方.net構(gòu)架,也沒有像ruby由于歷史較短,構(gòu)架開發(fā)的相對集中。Ruby on Rails 構(gòu)架開發(fā)中小型web程序天下無敵)。不過這也從另一個(gè)側(cè)面說明,python比較優(yōu)秀,吸引的人才多,項(xiàng)目也多。


3. Python應(yīng)用場景

Web應(yīng)用開發(fā)


Python經(jīng)常被用于Web開發(fā)。比如,通過mod_wsgi模塊,Apache可以運(yùn)行用Python編寫的Web程序。Python定義了WSGI標(biāo)準(zhǔn)應(yīng)用接口來協(xié)調(diào)Http服務(wù)器與基于Python的Web程序之間的通信。一些Web框架,如Django,TurboGears,web2py,Zope等,可以讓程序員輕松地開發(fā)和管理復(fù)雜的Web程序。

操作系統(tǒng)管理、服務(wù)器運(yùn)維的自動(dòng)化腳本

在很多操作系統(tǒng)里,Python是標(biāo)準(zhǔn)的系統(tǒng)組件。

大多數(shù)Linux發(fā)行版以及NetBSD、OpenBSD和Mac OS

X都集成了Python,可以在終端下直接運(yùn)行Python。有一些Linux發(fā)行版的安裝器使用Python語言編寫,比如Ubuntu的Ubiquity安裝器,Red

Hat Linux和Fedora的Anaconda安裝器。Gentoo

Linux使用Python來編寫它的Portage包管理系統(tǒng)。Python標(biāo)準(zhǔn)庫包含了多個(gè)調(diào)用操作系統(tǒng)功能的庫。通過pywin32這個(gè)第三方軟件

? 包,Python能夠訪問Windows的COM服務(wù)及其它Windows

API。使用IronPython,Python程序能夠直接調(diào)用.Net

Framework。一般說來,Python編寫的系統(tǒng)管理腳本在可讀性、性能、代碼重用度、擴(kuò)展性幾方面都優(yōu)于普通的shell腳本。

科學(xué)計(jì)算

NumPy,SciPy,Matplotlib可以讓Python程序員編寫科學(xué)計(jì)算程序。

桌面軟件

PyQt、PySide、wxPython、PyGTK是Python快速開發(fā)桌面應(yīng)用程序的利器。

服務(wù)器軟件(網(wǎng)絡(luò)軟件)

Python對于各種網(wǎng)絡(luò)協(xié)議的支持很完善,因此經(jīng)常被用于編寫服務(wù)器軟件、網(wǎng)絡(luò)爬蟲。第三方庫Twisted支持異步網(wǎng)絡(luò)編程和多數(shù)標(biāo)準(zhǔn)的網(wǎng)絡(luò)協(xié)議(包含客戶端和服務(wù)器),并且提供了多種工具,被廣泛用于編寫高性能的服務(wù)器軟件。

游戲

很多游戲使用C++編寫圖形顯示等高性能模塊,而使用Python或者Lua編寫游戲的邏輯、服務(wù)器。相較于Python,Lua的功能更簡單、體積更?。欢鳳ython則支持更多的特性和數(shù)據(jù)類型。

構(gòu)思實(shí)現(xiàn),產(chǎn)品早期原型和迭代

YouTube、Google、Yahoo!、NASA都在內(nèi)部大量地使用Python


Python Twisted介紹

Twisted是用Python實(shí)現(xiàn)的基于事件驅(qū)動(dòng)的網(wǎng)絡(luò)引擎框架。Twisted誕生于2000年初,在當(dāng)時(shí)的網(wǎng)絡(luò)游戲開發(fā)者看來,無論他們使用哪種語言,手中都鮮有可兼顧擴(kuò)展性及跨平臺(tái)的網(wǎng)絡(luò)庫。Twisted的作者試圖在當(dāng)時(shí)現(xiàn)有的環(huán)境下開發(fā)游戲,這一步走的非常艱難,他們迫切地需要一個(gè)可擴(kuò)展性高、基于事件驅(qū)動(dòng)、跨平臺(tái)的網(wǎng)絡(luò)開發(fā)框架,為此他們決定自己實(shí)現(xiàn)一個(gè),并從那些之前的游戲和網(wǎng)絡(luò)應(yīng)用程序的開發(fā)者中學(xué)習(xí),汲取他們的經(jīng)驗(yàn)教訓(xùn)。

Twisted支持許多常見的傳輸及應(yīng)用層協(xié)議,包括TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC以及FTP。就像Python一樣,Twisted也具有“內(nèi)置電池”(batteries-included)的特點(diǎn)。Twisted對于其支持的所有協(xié)議都帶有客戶端和服務(wù)器實(shí)現(xiàn),同時(shí)附帶有基于命令行的工具,使得配置和部署產(chǎn)品級的Twisted應(yīng)用變得非常方便。


21.1 為什么需要Twisted


2000年時(shí),Twisted的作者Glyph正在開發(fā)一個(gè)名為Twisted

Reality的基于文本方式的多人在線游戲。這個(gè)游戲采用Java開發(fā),里面盡是一堆線程——每個(gè)連接就有3個(gè)線程處理。處理輸入的線程會(huì)在讀操作上阻塞,處理輸出的線程將在一些寫操作上阻塞,還有一個(gè)“邏輯”線程將在等待定時(shí)器超時(shí)或者事件入隊(duì)列時(shí)休眠。隨著玩家們在虛擬世界中移動(dòng)并交互時(shí),線程出現(xiàn)死鎖,緩存被污染,程序中的加鎖邏輯幾乎從來就沒對過——采用多線程使得整個(gè)軟件變得復(fù)雜、漏洞百出而且極難擴(kuò)展。

為了尋求其他的解決方案,作者發(fā)現(xiàn)了Python,特別是Python中用于對流式對象比如socket和pipe進(jìn)行多路I/O復(fù)用的select模塊(UNIX規(guī)范第3版(SUSv3)描述了select)。那時(shí),Java并沒有提供操作系統(tǒng)的select接口或者任何其他的異步I/O

API(針對非阻塞式I/O的包java.nio已經(jīng)在J2SE

1.4中加入了,2002年發(fā)布)。通過用Python中的select模塊快速搭建起游戲的原型,這迅速降低了程序的復(fù)雜度,并且比多線程版本要更加可靠。

Glyph迅速轉(zhuǎn)向了Python、select以及基于事件驅(qū)動(dòng)的編程。他使用Python的select模塊為游戲編寫了客戶端和服務(wù)器。但他想要的還不止于此。從根本上說,他希望能將網(wǎng)絡(luò)行為轉(zhuǎn)變?yōu)閷τ螒蛑械膶ο蟮姆椒ㄕ{(diào)用。如果你能在游戲中收取郵件會(huì)怎樣,就像Nethack

mailer這種守護(hù)進(jìn)程一樣?如果游戲中的每位玩家都擁有一個(gè)主頁呢?Glyph發(fā)現(xiàn)他需要優(yōu)秀的IMAP以及HTTP客戶端和服務(wù)器的Python實(shí)現(xiàn),而這些都要采用select。

他首先轉(zhuǎn)向了Medusa,這是一個(gè)在90年代中期開發(fā)的平臺(tái),在這里可以采用Python中的asyncore模塊來編寫網(wǎng)絡(luò)服務(wù)。asyncore是一個(gè)異步化處理socket的模塊,在操作系統(tǒng)的select API之上構(gòu)建了一個(gè)調(diào)度器和回調(diào)接口。

這對于Glyph來說是個(gè)激動(dòng)人心的發(fā)現(xiàn),但Medusa有兩個(gè)缺點(diǎn):

這個(gè)項(xiàng)目到2001年就不再維護(hù)了,那正是glyph開發(fā)Twisted Reality的時(shí)候。

asyncore只是對socket的一個(gè)薄封裝層,應(yīng)用程序的編寫者仍然需要直接操作socket。這意味著程序可移植性的擔(dān)子仍然落在程序員自己身上。此外,那時(shí)asyncore對Windows的支持還有問題,Glyph希望能在Windows上運(yùn)行一個(gè)帶有圖形用戶界面的客戶端。

Glyph需要自己實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)引擎平臺(tái),而且他意識(shí)到Twisted Reality已經(jīng)打開了問題的大門,這和他的游戲一樣有趣。

隨著時(shí)間的推移,Twisted Reality這個(gè)游戲就演化成了Twisted網(wǎng)絡(luò)引擎平臺(tái)。它可以做到當(dāng)時(shí)Python中已有的網(wǎng)絡(luò)平臺(tái)所無法做到的事情:

使用基于事件驅(qū)動(dòng)的編程模型,而不是多線程模型。

跨平臺(tái):為主流操作系統(tǒng)平臺(tái)暴露出的事件通知系統(tǒng)提供統(tǒng)一的接口。

“內(nèi)置電池”的能力:提供流行的應(yīng)用層協(xié)議實(shí)現(xiàn),因此Twisted馬上就可為開發(fā)人員所用。

符合RFC規(guī)范,已經(jīng)通過健壯的測試套件證明了其一致性。

能很容易的配合多個(gè)網(wǎng)絡(luò)協(xié)議一起使用。

可擴(kuò)展。


21.2 Twisted架構(gòu)概覽


Twisted是一個(gè)事件驅(qū)動(dòng)型的網(wǎng)絡(luò)引擎。由于事件驅(qū)動(dòng)編程模型在Twisted的設(shè)計(jì)哲學(xué)中占有重要的地位,因此這里有必要花點(diǎn)時(shí)間來回顧一下究竟事件驅(qū)動(dòng)意味著什么。

事件驅(qū)動(dòng)編程是一種編程范式,這里程序的執(zhí)行流由外部事件來決定。它的特點(diǎn)是包含一個(gè)事件循環(huán),當(dāng)外部事件發(fā)生時(shí)使用回調(diào)機(jī)制來觸發(fā)相應(yīng)的處理。另外兩種常見的編程范式是(單線程)同步以及多線程編程。

讓我們用例子來比較和對比一下單線程、多線程以及事件驅(qū)動(dòng)編程模型。圖21.1展示了隨著時(shí)間的推移,這三種模式下程序所做的工作。這個(gè)程序有3個(gè)任務(wù)需要完成,每個(gè)任務(wù)都在等待I/O操作時(shí)阻塞自身。阻塞在I/O操作上所花費(fèi)的時(shí)間已經(jīng)用灰色框標(biāo)示出來了。


圖21.1 線程模型


在單線程同步模型中,任務(wù)按照順序執(zhí)行。如果某個(gè)任務(wù)因?yàn)镮/O而阻塞,其他所有的任務(wù)都必須等待,直到它完成之后它們才能依次執(zhí)行。這種明確的執(zhí)行順序和串行化處理的行為是很容易推斷得出的。如果任務(wù)之間并沒有互相依賴的關(guān)系,但仍然需要互相等待的話這就使得程序不必要的降低了運(yùn)行速度。

在多線程版本中,這3個(gè)任務(wù)分別在獨(dú)立的線程中執(zhí)行。這些線程由操作系統(tǒng)來管理,在多處理器系統(tǒng)上可以并行處理,或者在單處理器系統(tǒng)上交錯(cuò)執(zhí)行。這使得當(dāng)某個(gè)線程阻塞在某個(gè)資源的同時(shí)其他線程得以繼續(xù)執(zhí)行。與完成類似功能的同步程序相比,這種方式更有效率,但程序員必須寫代碼來保護(hù)共享資源,防止其被多個(gè)線程同時(shí)訪問。多線程程序更加難以推斷,因?yàn)檫@類程序不得不通過線程同步機(jī)制如鎖、可重入函數(shù)、線程局部存儲(chǔ)或者其他機(jī)制來處理線程安全問題,如果實(shí)現(xiàn)不當(dāng)就會(huì)導(dǎo)致出現(xiàn)微妙且令人痛不欲生的bug。

在事件驅(qū)動(dòng)版本的程序中,3個(gè)任務(wù)交錯(cuò)執(zhí)行,但仍然在一個(gè)單獨(dú)的線程控制中。當(dāng)處理I/O或者其他昂貴的操作時(shí),注冊一個(gè)回調(diào)到事件循環(huán)中,然后當(dāng)I/O操作完成時(shí)繼續(xù)執(zhí)行。回調(diào)描述了該如何處理某個(gè)事件。事件循環(huán)輪詢所有的事件,當(dāng)事件到來時(shí)將它們分配給等待處理事件的回調(diào)函數(shù)。這種方式讓程序盡可能的得以執(zhí)行而不需要用到額外的線程。事件驅(qū)動(dòng)型程序比多線程程序更容易推斷出行為,因?yàn)槌绦騿T不需要關(guān)心線程安全問題。

當(dāng)我們面對如下的環(huán)境時(shí),事件驅(qū)動(dòng)模型通常是一個(gè)好的選擇:

程序中有許多任務(wù),而且…

任務(wù)之間高度獨(dú)立(因此它們不需要互相通信,或者等待彼此)而且…

在等待事件到來時(shí),某些任務(wù)會(huì)阻塞。

當(dāng)應(yīng)用程序需要在任務(wù)間共享可變的數(shù)據(jù)時(shí),這也是一個(gè)不錯(cuò)的選擇,因?yàn)檫@里不需要采用同步處理。

網(wǎng)絡(luò)應(yīng)用程序通常都有上述這些特點(diǎn),這使得它們能夠很好的契合事件驅(qū)動(dòng)編程模型。

重用已有的應(yīng)用

在Twisted創(chuàng)建之前就已經(jīng)有了許多針對多種流行的網(wǎng)絡(luò)協(xié)議的客戶端和服務(wù)器實(shí)現(xiàn)了。為什么Glyph不直接用Apache、IRCd、BIND、OpenSSH或者任何其他已有的應(yīng)用,而要為Twisted從頭開始重新實(shí)現(xiàn)各個(gè)協(xié)議的客戶端和服務(wù)器呢?

問題在于所有這些已有的實(shí)現(xiàn)都存在有從頭寫起的網(wǎng)絡(luò)層代碼,通常都是C代碼。而應(yīng)用層代碼直接同網(wǎng)絡(luò)層耦合在一起,這使得它們非常難以以庫的形式來復(fù)用。當(dāng)要一起使用這些組件時(shí),如果希望在多個(gè)協(xié)議中暴露相同的數(shù)據(jù),則它們必須以黑盒的形式來看待,這使得開發(fā)者根本沒機(jī)會(huì)重用代碼。此外,服務(wù)器和客戶端的實(shí)現(xiàn)通常是分離的,彼此之間不共享代碼。要擴(kuò)展這些應(yīng)用,維護(hù)跨平臺(tái)的客戶端-服務(wù)器兼容性的難度本不至于這么大。

Twisted中的客戶端和服務(wù)器是用Python開發(fā)的,采用了一致性的接口。這使得開發(fā)新的客戶端和服務(wù)器變得很容易實(shí)現(xiàn),可以在客戶端和服務(wù)器之間共享代碼,在協(xié)議之間共享應(yīng)用邏輯,以及對某個(gè)實(shí)現(xiàn)的代碼做測試。

Reactor模式

Twisted實(shí)現(xiàn)了設(shè)計(jì)模式中的反應(yīng)堆(reactor)模式,這種模式在單線程環(huán)境中調(diào)度多個(gè)事件源產(chǎn)生的事件到它們各自的事件處理例程中去。

Twisted的核心就是reactor事件循環(huán)。Reactor可以感知網(wǎng)絡(luò)、文件系統(tǒng)以及定時(shí)器事件。它等待然后處理這些事件,從特定于平臺(tái)的行為中抽象出來,并提供統(tǒng)一的接口,使得在網(wǎng)絡(luò)協(xié)議棧的任何位置對事件做出響應(yīng)都變得簡單。


基本上reactor完成的任務(wù)就是:


while True:

timeout=time_until_next_timed_event()

events=wait_for_events(timeout)

events+= timed_events_until(now())

for event in events:

event.process()

Twisted目前在所有平臺(tái)上的默認(rèn)reactor都是基于poll

API的(UNIX規(guī)范第3版(SUSv3)中描述)。此外,Twisted還支持一些特定于平臺(tái)的高容量多路復(fù)用API。這些reactor包括基于FreeBSD中kqueue機(jī)制的KQueue

reactor,支持epoll接口的系統(tǒng)(目前是Linux 2.6)中的epoll

reactor,以及基于Windows下的輸入輸出完成端口的IOCP reactor。

在實(shí)現(xiàn)輪詢的相關(guān)細(xì)節(jié)中,Twisted需要考慮的包括:

網(wǎng)絡(luò)和文件系統(tǒng)的限制

緩沖行為

如何檢測連接丟失

出現(xiàn)錯(cuò)誤時(shí)的返回值

Twisted的reactor實(shí)現(xiàn)同時(shí)也考慮了正確使用底層的非阻塞式API,并正確處理各種邊界情況。由于Python中沒有暴露出IOCP API,因此Twisted需要維護(hù)自己的實(shí)現(xiàn)。

管理回調(diào)鏈

回調(diào)是事件驅(qū)動(dòng)編程模型中的基礎(chǔ),也是reactor通知應(yīng)用程序事件已經(jīng)處理完成的方式。隨著程序規(guī)模不斷擴(kuò)大,基于事件驅(qū)動(dòng)的程序需要同時(shí)處理事件處理成功和出錯(cuò)的情況,這使得程序變得越來越復(fù)雜。若沒有注冊一個(gè)合適的回調(diào),程序就會(huì)阻塞,因?yàn)檫@個(gè)事件處理的過程絕不會(huì)發(fā)生。出現(xiàn)錯(cuò)誤時(shí)需要通過應(yīng)用程序的不同層次從網(wǎng)絡(luò)棧向上傳遞回調(diào)鏈。


下面是兩段Python偽碼,分別是同步和異步模式下獲取URL的玩具代碼。讓我們相互比較一下這兩個(gè)版本,看看基于事件驅(qū)動(dòng)的程序有什么缺陷:

以同步的方式獲取URL:


import getPage

defprocessPage(page):

print page

deflogError(error):

print error

deffinishProcessing(value):

print "Shutting down..."

exit(0)

url= "http://google.com"

try:

page= getPage(url)

processPage(page)

except Error, e:

logError(error)

finally:

finishProcessing()

以異步的方式獲取URL:

from twisted.internet import reactor

import getPage

defprocessPage(page):

print page

finishProcessing()

deflogError(error):

print error

finishProcessing()

deffinishProcessing(value):

print "Shutting down..."

reactor.stop()

url= "http://google.com"

# getPage takes: url,

# success callback, error callback

getPage(url, processPage, logError)

reactor.run()


在異步版的URL獲取器中,reactor.run()啟動(dòng)reactor事件循環(huán)。在同步和異步版程序中,我們假定getPage函數(shù)處理獲取頁面的工作。如果獲取成功就調(diào)用processPage,如果嘗試獲取頁面時(shí)出現(xiàn)了Exception(異常),logError就得到調(diào)用。無論哪種情況,最后都要調(diào)用finishProcessing。

異步版中的logError回調(diào)正對應(yīng)于同步版中的try/except塊。對processPage的回調(diào)對應(yīng)于else塊,無條件回調(diào)的finishProcessing就對應(yīng)于finally塊。

在同步版中,代碼結(jié)構(gòu)直接顯示出有一個(gè)try/except塊,logError和processPage這兩者間只會(huì)取其一調(diào)用一次,而finishProcessing總是會(huì)被調(diào)用一次。在異步版中需要由程序員自己負(fù)責(zé)正確調(diào)用成功和失敗情況下的回調(diào)鏈。如果由于編程錯(cuò)誤,在processPage或者logError的回調(diào)鏈之后沒有調(diào)用finishProcessing,reactor事件循環(huán)將永遠(yuǎn)不會(huì)停止,程序就會(huì)卡住。

這個(gè)玩具式的例子告訴我們在開發(fā)Twisted的頭幾年里這種復(fù)雜性令程序員感到非常沮喪。而Twisted應(yīng)對這種復(fù)雜性的方式是新增一個(gè)稱為Deferred(延遲)的對象。

Deferreds

Deferred對象以抽象化的方式表達(dá)了一種思想,即結(jié)果還尚不存在。它同樣能夠幫助管理產(chǎn)生這個(gè)結(jié)果所需要的回調(diào)鏈。當(dāng)從函數(shù)中返回時(shí),Deferred對象承諾在某個(gè)時(shí)刻函數(shù)將產(chǎn)生一個(gè)結(jié)果。返回的Deferred對象中包含所有注冊到事件上的回調(diào)引用,因此在函數(shù)間只需要傳遞這一個(gè)對象即可,跟蹤這個(gè)對象比單獨(dú)管理所有的回調(diào)要簡單的多。

Deferred對象包含一對回調(diào)鏈,一個(gè)是針對操作成功的回調(diào),一個(gè)是針對操作失敗的回調(diào)。初始狀態(tài)下Deferred對象的兩條鏈都為空。在事件處理的過程中,每個(gè)階段都為其添加處理成功的回調(diào)和處理失敗的回調(diào)。當(dāng)一個(gè)異步結(jié)果到來時(shí),Deferred對象就被“激活”,那么處理成功的回調(diào)和處理失敗的回調(diào)就可以以合適的方式按照它們添加進(jìn)來的順序依次得到調(diào)用。


異步版URL獲取器采用Deferred對象后的代碼如下:

from twisted.internet import reactor

import getPage

defprocessPage(page):

print page

deflogError(error):

print error

deffinishProcessing(value):

print "Shutting down..."

reactor.stop()

url= "http://google.com"

deferred= getPage(url) # getPage returns a Deferred

deferred.addCallbacks(success, failure)

deferred.addBoth(stop)

reactor.run()

在這個(gè)版本中調(diào)用的事件處理函數(shù)與之前相同,但它們都注冊到了一個(gè)單獨(dú)的Deferred對象上,而不是分散在代碼各處再以參數(shù)形式傳遞給getPage。

Deferred對象創(chuàng)建時(shí)包含兩個(gè)添加回調(diào)的階段。第一階段,addCallbacks將

processPage和logError添加到它們各自歸屬的回調(diào)鏈中。然后addBoth再將finishProcessing同時(shí)添加到這兩個(gè)回調(diào)鏈上。用圖解的方式來看,回調(diào)鏈應(yīng)該如圖21.2所示:

圖21.2 回調(diào)鏈

Deferred對象只能被激活一次,如果試圖重復(fù)激活將引發(fā)一個(gè)異常。這使得Deferred對象的語義相當(dāng)接近于同步版中的try/except塊。從而讓異步事件的處理能更容易推斷,避免由于針對單個(gè)事件的回調(diào)調(diào)用多了一個(gè)或少了一個(gè)而產(chǎn)生微妙的bug。

理解Deferred對象對于理解Twisted程序的執(zhí)行流是非常重要的。然而當(dāng)使用Twisted為我們提供的針對網(wǎng)絡(luò)協(xié)議的高層抽象時(shí),通常情況下我們完全不需要直接使用Deferred對象。

Deferred對象所包含的抽象概念是非常強(qiáng)大的,這種思想已經(jīng)被許多其他的事件驅(qū)動(dòng)平臺(tái)所借用,包括jQuery、Dojo和Mochikit。

Transports

Transports代表網(wǎng)絡(luò)中兩個(gè)通信結(jié)點(diǎn)之間的連接。Transports負(fù)責(zé)描述連接的細(xì)節(jié),比如連接是面向流式的還是面向數(shù)據(jù)報(bào)的,流控以及可靠性。TCP、UDP和Unix套接字可作為transports的例子。它們被設(shè)計(jì)為“滿足最小功能單元,同時(shí)具有最大程度的可復(fù)用性”,而且從協(xié)議實(shí)現(xiàn)中分離出來,這讓許多協(xié)議可以采用相同類型的傳輸。Transports實(shí)現(xiàn)了ITransports接口,它包含如下的方法:

write以非阻塞的方式按順序依次將數(shù)據(jù)寫到物理連接上

writeSequence將一個(gè)字符串列表寫到物理連接上

loseConnection將所有掛起的數(shù)據(jù)寫入,然后關(guān)閉連接

getPeer取得連接中對端的地址信息

getHost取得連接中本端的地址信息

將transports從協(xié)議中分離出來也使得對這兩個(gè)層次的測試變得更加簡單??梢酝ㄟ^簡單地寫入一個(gè)字符串來模擬傳輸,用這種方式來檢查。

Protocols

Protocols描述了如何以異步的方式處理網(wǎng)絡(luò)中的事件。HTTP、DNS以及IMAP是應(yīng)用層協(xié)議中的例子。Protocols實(shí)現(xiàn)了IProtocol接口,它包含如下的方法:

makeConnection在transport對象和服務(wù)器之間建立一條連接

connectionMade連接建立起來后調(diào)用

dataReceived接收數(shù)據(jù)時(shí)調(diào)用

connectionLost關(guān)閉連接時(shí)調(diào)用

我們最好以一個(gè)例子來說明reactor、protocols以及transports這三者之間的關(guān)系。以下是完整的echo服務(wù)器和客戶端的實(shí)現(xiàn),首先來看看服務(wù)器部分:

from twisted.internet import protocol, reactor

class Echo(protocol.Protocol):

defdataReceived(self, data):

# As soon as any data is received, write it back

self.transport.write(data)

class EchoFactory(protocol.Factory):

defbuildProtocol(self, addr):

return Echo()

reactor.listenTCP(8000, EchoFactory())

reactor.run()

接著是客戶端部分:

from twisted.internet import reactor, protocol

class EchoClient(protocol.Protocol):

defconnectionMade(self):

self.transport.write("hello, world!")

defdataReceived(self, data):

print "Server said:", data

self.transport.loseConnection()

defconnectionLost(self, reason):

print "connection lost"

class EchoFactory(protocol.ClientFactory):

defbuildProtocol(self, addr):

return EchoClient()

defclientConnectionFailed(self, connector, reason):

print "Connection failed - goodbye!"

reactor.stop()

defclientConnectionLost(self, connector, reason):

print "Connection lost - goodbye!"

reactor.stop()

reactor.connectTCP("localhost", 8000, EchoFactory())

reactor.run()

運(yùn)行服務(wù)器端腳本將啟動(dòng)一個(gè)TCP服務(wù)器,監(jiān)聽端口8000上的連接。服務(wù)器采用的是Echo協(xié)議,數(shù)據(jù)經(jīng)TCP

transport對象寫出。運(yùn)行客戶端腳本將對服務(wù)器發(fā)起一個(gè)TCP連接,回顯服務(wù)器端的回應(yīng)然后終止連接并停止reactor事件循環(huán)。這里的Factory用來對連接的雙方生成protocol對象實(shí)例。兩端的通信是異步的,connectTCP負(fù)責(zé)注冊回調(diào)函數(shù)到reactor事件循環(huán)中,當(dāng)socket上有數(shù)據(jù)可讀時(shí)通知回調(diào)處理。

Applications

Twisted是用來創(chuàng)建具有可擴(kuò)展性、跨平臺(tái)的網(wǎng)絡(luò)服務(wù)器和客戶端的引擎。在生產(chǎn)環(huán)境中,以標(biāo)準(zhǔn)化的方式簡化部署這些應(yīng)用的過程對于Twisted這種被廣泛采用的平臺(tái)來說是非常重要的一環(huán)。為此,Twisted開發(fā)了一套應(yīng)用程序基礎(chǔ)組件,采用可重用、可配置的方式來部署Twisted應(yīng)用。這種方式使程序員避免堆砌千篇一律的代碼來將應(yīng)用程序同已有的工具整合在一起,這包括精靈化進(jìn)程(daemonization)、日志處理、使用自定義的reactor循環(huán)、對代碼做性能剖析等。

應(yīng)用程序基礎(chǔ)組件包含4個(gè)主要部分:服務(wù)(Service)、應(yīng)用(Application)、配置管理(通過TAC文件和插件)以及twistd命令行程序。為了說明這個(gè)基礎(chǔ)組件,我們將上一節(jié)的Echo服務(wù)器轉(zhuǎn)變成一個(gè)應(yīng)用。

Service

Service就是IService接口下實(shí)現(xiàn)的可以啟動(dòng)和停止的組件。Twisted自帶有TCP、FTP、HTTP、SSH、DNS等服務(wù)以及其他協(xié)議的實(shí)現(xiàn)。其中許多Service都可以注冊到單獨(dú)的應(yīng)用中。IService接口的核心是:

startService啟動(dòng)服務(wù)。可能包含加載配置數(shù)據(jù),設(shè)定數(shù)據(jù)庫連接或者監(jiān)聽某個(gè)端口

stopService關(guān)閉服務(wù)。可能包含將狀態(tài)保存到磁盤,關(guān)閉數(shù)據(jù)庫連接或者停止監(jiān)聽端口

我們的Echo服務(wù)使用TCP協(xié)議,因此我們可以使用Twisted中IService接口下默認(rèn)的TCPServer實(shí)現(xiàn)。

Application

Application是處于最頂層的Service,代表了整個(gè)Twisted應(yīng)用程序。Service需要將其自身同Application注冊,然后就可以用下面我們將介紹的部署工具twistd搜索并運(yùn)行應(yīng)用程序。我們將創(chuàng)建一個(gè)可以同Echo

Service注冊的Echo應(yīng)用。

TAC文件

當(dāng)在一個(gè)普通的Python文件中管理Twisted應(yīng)用程序時(shí),需要由開發(fā)者負(fù)責(zé)編寫啟動(dòng)和停止reactor事件循環(huán)以及配置應(yīng)用程序的代碼。在Twisted的基礎(chǔ)組件中,協(xié)議的實(shí)現(xiàn)都是在一個(gè)模塊中完成的,需要使用到這些協(xié)議的Service可以注冊到一個(gè)Twisted應(yīng)用程序配置文件中(TAC文件)去,這樣reactor事件循環(huán)和程序配置就可以由外部組件來進(jìn)行管理。

要將我們的Echo服務(wù)器轉(zhuǎn)變成一個(gè)Echo應(yīng)用,我們可以按照以下幾個(gè)簡單的步驟來完成:

將Echo服務(wù)器的Protocol部分移到它們自己所歸屬的模塊中去。

在TAC文件中:

創(chuàng)建一個(gè)Echo應(yīng)用。

創(chuàng)建一個(gè)TCPServer的Service實(shí)例,它將使用我們的EchoFactory,然后同前面創(chuàng)建的應(yīng)用完成注冊。

管理reactor事件循環(huán)的代碼將由twistd來負(fù)責(zé),我們下面會(huì)對此進(jìn)行討論。這樣,應(yīng)用程序的代碼就變成這樣了:

echo.py文件:

from twisted.internet import protocol, reactor

class Echo(protocol.Protocol):

defdataReceived(self, data):

self.transport.write(data)

class EchoFactory(protocol.Factory):

defbuildProtocol(self, addr):

return Echo()

twistd

twistd(讀作“twist-dee”)是一個(gè)跨平臺(tái)的用來部署Twisted應(yīng)用程序的工具。它執(zhí)行TAC文件并負(fù)責(zé)處理啟動(dòng)和停止應(yīng)用程序。作為Twisted在網(wǎng)絡(luò)編程中具有“內(nèi)置電池”能力的一部分,twistd自帶有一些非常有用的配置標(biāo)志,包括將應(yīng)用程序轉(zhuǎn)變?yōu)槭刈o(hù)進(jìn)程、定義日志文件的路徑、設(shè)定特權(quán)級別、在chroot下運(yùn)行、使用非默認(rèn)的reactor,甚至是在profiler下運(yùn)行應(yīng)用程序。

我們可以像這樣運(yùn)行這個(gè)Echo服務(wù)應(yīng)用:

$ twistd –y echo_server.tac

在這個(gè)簡單的例子里,twistd將這個(gè)應(yīng)用程序作為守護(hù)進(jìn)程來啟動(dòng),日志記錄在twistd.log文件中。啟動(dòng)和停止應(yīng)用后,日志文件內(nèi)容如下:

2011-11-19 22:23:07-0500 [-] Logopened.

2011-11-19 22:23:07-0500 [-]twistd11.0.0 (/usr/bin/python2.7.1)startingup.

2011-11-19 22:23:07-0500 [-]reactor class:twisted.internet.selectreactor.SelectReactor.

2011-11-19 22:23:07-0500 [-]echo.EchoFactorystartingon8000

2011-11-19 22:23:07-0500 [-] Startingfactory<echo.EchoFactoryinstanceat0x12d8670>

2011-11-19 22:23:20-0500 [-] Received SIGTERM, shutting down.

2011-11-19 22:23:20-0500 [-] (TCP Port 8000 Closed)

2011-11-19 22:23:20-0500 [-] Stopping factory <echo.EchoFactory instance at 0x12d8670>

2011-11-19 22:23:20-0500 [-] Main loop terminated.

2011-11-19 22:23:20-0500 [-] Server Shut Down.

通過使用Twisted框架中的基礎(chǔ)組件來運(yùn)行服務(wù),這么做使得開發(fā)人員能夠不用再編寫類似守護(hù)進(jìn)程和記錄日志這樣的冗余代碼了。這同樣也為部署應(yīng)用程序建立了一個(gè)標(biāo)準(zhǔn)的命令行接口。

Plugins

對于運(yùn)行Twisted應(yīng)用程序的方法,除了基于TAC文件外還有一種可選的方法,這就是插件系統(tǒng)。TAC系統(tǒng)可以很方便的將Twisted預(yù)定義的服務(wù)同應(yīng)用程序配置文件注冊,而插件系統(tǒng)能夠方便的將用戶自定義的服務(wù)注冊為twistd工具的子命令,然后擴(kuò)展應(yīng)用程序的命令行接口。

在使用插件系統(tǒng)時(shí):

由于只有plugin API需要保持穩(wěn)定,這使得第三方開發(fā)者能很容易地?cái)U(kuò)展軟件。

插件發(fā)現(xiàn)能力已經(jīng)集成到系統(tǒng)中了。插件可以在程序首次運(yùn)行時(shí)加載并保存,每次程序啟動(dòng)時(shí)會(huì)重新觸發(fā)插件發(fā)現(xiàn)過程,或者也可以在程序運(yùn)行期間反復(fù)輪詢新插件,這使得在程序已經(jīng)啟動(dòng)后我們還可以判斷是否有新的插件安裝上了。

當(dāng)使用Twisted插件系統(tǒng)來擴(kuò)展軟件時(shí),我們要做的就是創(chuàng)建IPlugin接口下實(shí)現(xiàn)的對象并將它們放到一個(gè)特定的位置中,這里插件系統(tǒng)知道該如何去找到它們。

我們已經(jīng)將Echo服務(wù)轉(zhuǎn)換為一個(gè)Twisted應(yīng)用程序了,而將其轉(zhuǎn)換為一個(gè)Twisted插件也是非常簡單直接的。在我們之前的Echo模塊中,除了包含有Echo協(xié)議和EchoFactory的定義之外,現(xiàn)在我們還要添加一個(gè)名為twistd的目錄,其中還包含著一個(gè)名為plugins的子目錄,這里正是我們需要定義echo插件的地方。通過這個(gè)插件,我們可以啟動(dòng)一個(gè)echo服務(wù),并將需要使用的端口號作為參數(shù)指定給twistd工具。

from zope.interface import implements

from twisted.python import usage

from twisted.plugin import IPlugin

from twisted.application.service import IServiceMaker

from twisted.application import internet

from echo import EchoFactory

class Options(usage.Options):

optParameters= [["port", "p", 8000, "The port number to listen on."]]

class EchoServiceMaker(object):

implements(IServiceMaker, IPlugin)

tapname= "echo"

description= "A TCP-based echo server."

options= Options

defmakeService(self, options):

"""

? ? Construct a TCPServer from a factory defined in myproject.

? ? """

return internet.TCPServer(int(options["port"]), EchoFactory())

serviceMaker= EchoServiceMaker()

現(xiàn)在,我們的Echo服務(wù)器將作為一個(gè)服務(wù)選項(xiàng)出現(xiàn)在twistd –help的輸出中。運(yùn)行twistd echo –port=1235將在端口1235上啟動(dòng)一個(gè)Echo服務(wù)器。

Twisted還帶有一個(gè)可拔插的針對服務(wù)器端認(rèn)證的模塊twisted.cred,插件系統(tǒng)常見的用途就是為應(yīng)用程序添加一個(gè)認(rèn)證模式。我們可以使用twisted.cred中現(xiàn)成的AuthOptionMixin類來添加針對各種認(rèn)證的命令行支持,或者是添加新的認(rèn)證類型。比如,我們可以使用插件系統(tǒng)來添加基于本地Unix密碼數(shù)據(jù)庫或者是基于LDAP服務(wù)器的認(rèn)證方式。

twistd工具中附帶有許多Twisted所支持的協(xié)議插件,只用一條單獨(dú)的命令就可以完成啟動(dòng)服務(wù)器的工作了。這里有一些通過twistd啟動(dòng)服務(wù)器的例子:

twistdweb–port 8080 –path .

這條命令將在8080端口啟動(dòng)一個(gè)HTTP服務(wù)器,在當(dāng)前目錄中負(fù)責(zé)處理靜態(tài)和動(dòng)態(tài)頁面請求。

twistd dns –p 5553 –hosts-file=hosts

這條命令在端口5553上啟動(dòng)一個(gè)DNS服務(wù)器,解析指定的文件hosts中的域名,這個(gè)文件的內(nèi)容格式同/etc/hosts一樣。

sudo twistd conch –ptcp:2222

這條命令在端口2222上啟動(dòng)一個(gè)SSH服務(wù)器。ssh的密鑰必須獨(dú)立設(shè)定。

twistdmail –E –H localhost –d localhost=emails

這條命令啟動(dòng)一個(gè)ESMTP POP3服務(wù)器,為本地主機(jī)接收郵件并保存到指定的emails目錄下。

我們可以方便的通過twistd來搭建一個(gè)用于測試客戶端功能的服務(wù)器,但它同樣是可裝載的、產(chǎn)品級的服務(wù)器實(shí)現(xiàn)。

在部署應(yīng)用程序的方式上,Twisted通過TAC文件、插件以及命令行工具twistd的部署方式已經(jīng)獲得了成功。但是有趣的是,對于大多數(shù)大型Twisted應(yīng)用程序來說,部署它們?nèi)匀恍枰貙懸恍┻@類管理和監(jiān)控組件;Twisted的架構(gòu)并沒有對系統(tǒng)管理員的需求呈現(xiàn)出太多的友好性。這也反映了一個(gè)事實(shí),那就是對于系統(tǒng)管理員來說Twisted歷來就沒有太多架構(gòu)可言,而這些系統(tǒng)管理員才是部署和維護(hù)應(yīng)用程序的專家。在這方面,Twisted在未來架構(gòu)設(shè)計(jì)的決策上需要更積極的征求這類專家級用戶的反饋意見。

21.3 反思與教訓(xùn)

Twisted最近剛剛渡過了其10周年的誕辰。自項(xiàng)目成立以來,由于受2000年早期的網(wǎng)絡(luò)游戲啟發(fā),目前的Twisted已經(jīng)在很大程度上實(shí)現(xiàn)了作為一個(gè)可擴(kuò)展、跨平臺(tái)、事件驅(qū)動(dòng)的網(wǎng)絡(luò)引擎的目標(biāo)。Twisted廣泛使用于生產(chǎn)環(huán)境中,從Google、盧卡斯電影到Justin.TV以及Launchpad軟件協(xié)作平臺(tái)都有在使用。Twisted中的服務(wù)器端實(shí)現(xiàn)是多個(gè)開源軟件的核心,包括BuildBot、BitTorrent以及TahoeLAFS。

Twisted從最初開發(fā)到現(xiàn)在,其架構(gòu)已經(jīng)經(jīng)歷了幾次大的變動(dòng)。Deferred對象作為一個(gè)關(guān)鍵部分被增加了進(jìn)來。如前文所述,這是用來管理延后的結(jié)果以及相應(yīng)的回調(diào)鏈。

還有一個(gè)重要的部分被移除掉了,在目前的實(shí)現(xiàn)中已經(jīng)幾乎看不到任何影子了,這就是Twisted應(yīng)用持久化(Twisted Application Persistence)。

Twisted應(yīng)用持久化

Twisted應(yīng)用持久化(TAP)是指將應(yīng)用程序的配置和狀態(tài)保存在一個(gè)pickle中。要運(yùn)行采用了這種方案的應(yīng)用需要兩個(gè)步驟:

使用mktap工具創(chuàng)建一個(gè)代表該應(yīng)用的pickle(該工具現(xiàn)已廢棄不用)。

使用twistd命令行工具進(jìn)行unpickle操作,然后運(yùn)行該應(yīng)用。

這個(gè)過程是受Smalltalk images的啟發(fā),因?yàn)槲覀冇憛捘欠N臨時(shí)性的且難以使用的專用配置語言,不希望它們在項(xiàng)目中不斷擴(kuò)散。我們更希望在Python中表示配置的細(xì)節(jié)。

很快,TAP文件就引入了不必要的復(fù)雜性。修改Twisted中的類并不會(huì)使pickle中這些類的實(shí)例得到改變。在pickle對象上使用新版本的類方法或?qū)傩詴r(shí)可能會(huì)使整個(gè)應(yīng)用崩潰。因此“升級版”的概念得以引入,即將pickle對象升級到新的API版本。但這就會(huì)出現(xiàn)升級版本的矩陣化現(xiàn)象,出現(xiàn)各種不同版本的pickle對象,因此單元測試時(shí)需要維護(hù)涵蓋所有可能的升級路徑。想全面地跟蹤所有的接口變化依然很難,而且容易出錯(cuò)。

TAP以及相關(guān)的組件全部被廢除了,最終從Twisted中完全剔除掉。取而代之的是TAC文件和插件系統(tǒng)。TAP這個(gè)縮寫被重新定義為Twisted

Application Plugin(Twisted應(yīng)用插件),如今已經(jīng)很難在Twisted中找到pickle系統(tǒng)的蹤跡了。

我們從TAP的慘敗中得到的教訓(xùn)是:如果可維護(hù)性要達(dá)到合理化的程度,則持久性數(shù)據(jù)就需要有一個(gè)明確的模式。更一般的是,我們學(xué)到了如何為項(xiàng)目增加復(fù)雜度:為了解決某個(gè)問題而需要引入一個(gè)新系統(tǒng)時(shí),我們要正確理解這個(gè)方案的復(fù)雜性,并經(jīng)過測試。新系統(tǒng)所帶來的價(jià)值應(yīng)該明顯大于其復(fù)雜性。確保了這一點(diǎn)之后我們才能將方案付諸于項(xiàng)目中。

web2:重構(gòu)的教訓(xùn)

雖然這基本上不屬于架構(gòu)設(shè)計(jì)上的決策,但從項(xiàng)目管理的角度來看,重寫Twisted的Web實(shí)現(xiàn)對于Twisted的外在形象以及維護(hù)者對代碼庫中其他部分做架構(gòu)改善的能力卻有著長遠(yuǎn)的影響,因此這里值得我們簡單討論一下。

在2000年中期,Twisted的開發(fā)者決定完全重寫twisted.web

API,在Twisted代碼庫中將其作為一個(gè)單獨(dú)的項(xiàng)目實(shí)現(xiàn),這就是web2。web2將包含許多針對原有twisted.web的改善和提升,包括完全支持HTTP1.1,以及對流式數(shù)據(jù)的API支持。

web2最初只是試驗(yàn)性的項(xiàng)目,但最終被大型項(xiàng)目所采用,甚至意外的得以在Debian系統(tǒng)上打包發(fā)布。twisted.web和web2的開發(fā)一直并行持續(xù)了多年,新用戶常常被這兩個(gè)并行的項(xiàng)目搞混,關(guān)于究竟應(yīng)該使用哪種實(shí)現(xiàn)缺乏明確的提示,這使得新用戶很沮喪。轉(zhuǎn)換到web2的情況從未出現(xiàn),終于在2011年開發(fā)者將其從代碼庫中移除,官方主頁上再也看不到它了。web2中做出的一些改進(jìn)也被慢慢地移植回twisted.web中。

Twisted獲得了難以導(dǎo)航且結(jié)構(gòu)混亂,容易使新開發(fā)者感到困惑的“惡名”,這個(gè)印象部分歸功于web2。以至于數(shù)年之后,Twisted社區(qū)仍然在同這種不和諧的名聲做斗爭。

我們從web2中汲取的教訓(xùn)是:從頭開始重構(gòu)一個(gè)項(xiàng)目通常都是糟糕的主意。但如果必須這么做,請確保開發(fā)者社區(qū)能夠懂得這么做的長遠(yuǎn)意義,而且在用戶社群中要有明確的選擇該使用哪種實(shí)現(xiàn)。

如果Twisted能夠倒退回web2的時(shí)代,開發(fā)者們應(yīng)該會(huì)對twisted.web做一系列向后兼容型的修改而不是去重構(gòu)。

緊跟互聯(lián)網(wǎng)的浪潮

我們使用互聯(lián)網(wǎng)的方式還在持續(xù)演進(jìn)中。把多種協(xié)議的實(shí)現(xiàn)作為軟件核心的一部分,這個(gè)技術(shù)決策使得Twisted背負(fù)了維護(hù)這些協(xié)議的沉重負(fù)擔(dān)。隨著標(biāo)準(zhǔn)的改變以及對新協(xié)議的采納,原有的實(shí)現(xiàn)必須跟著演進(jìn),同時(shí)需要嚴(yán)格的保證向后兼容性。

Twisted基本上是一個(gè)志愿者驅(qū)動(dòng)型的項(xiàng)目,項(xiàng)目發(fā)展的限制因素不是技術(shù)社區(qū)的熱情,而在于志愿者的時(shí)間。比如說,1999年的RFC

2616中定義了HTTP 1.1規(guī)范,而在Twisted的HTTP協(xié)議實(shí)現(xiàn)中增加對HTTP

1.1的支持卻在2005年才開始,等到完成時(shí)已經(jīng)是2009年了。1998年RFC

2460中定義了對IPv6的支持,而Twisted對其的支持還在進(jìn)行中,但是直到2011年都未能合并進(jìn)去。

隨著所支持的操作系統(tǒng)的接口改變,實(shí)現(xiàn)也要跟著演進(jìn)。比如,epoll事件通知機(jī)制是在2002年加入到Linux

2.5.44版中的,Twisted隨之也發(fā)展出基于epoll的reactor事件循環(huán)來利用這個(gè)新的系統(tǒng)接口。2007年時(shí),蘋果公司發(fā)布的OS

10.5

Leopard系統(tǒng)中,系統(tǒng)調(diào)用poll的實(shí)現(xiàn)居然不支持外設(shè),對于蘋果公司來說這個(gè)問題足以讓他們在系統(tǒng)自帶的Python中屏蔽掉select.poll接口。Twisted不得不自行解決這個(gè)問題,并從那時(shí)起就對用戶提供文檔說明。

有時(shí)候,Twisted的開發(fā)并沒有緊跟網(wǎng)絡(luò)世界的變化,有一些改進(jìn)被移到核心層之外的程序庫中去了。比如Wokkel

project,這是對Twisted的Jabber/XMPP支持的改進(jìn)合集,已經(jīng)作為“待合入”的獨(dú)立項(xiàng)目有幾年之久了,但還沒有看到合入的希望。在2009年也曾經(jīng)嘗試過增加WebSocket到Twisted中,因?yàn)闉g覽器已經(jīng)開始采納對新協(xié)議的支持了。但開發(fā)計(jì)劃最終卻轉(zhuǎn)到其他外部項(xiàng)目中去了,因?yàn)殚_發(fā)者們決定暫不包含新的協(xié)議,直到IETF把它從草案轉(zhuǎn)變成標(biāo)準(zhǔn)以后再說。

所有這一切都在說明,庫和附加組件的擴(kuò)散有力的證明了Twisted的靈活性和可擴(kuò)展性。通過采用嚴(yán)格的測試驅(qū)動(dòng)開發(fā)策略以及文檔化和編碼規(guī)范標(biāo)準(zhǔn),這樣做能夠幫助項(xiàng)目避免出現(xiàn)需要“回爐”的情況。在維護(hù)大量所支持的協(xié)議和平臺(tái)的同時(shí)保持向后兼容性。Twisted是一個(gè)成熟、穩(wěn)定的項(xiàng)目,并繼續(xù)保持有非?;钴S的開發(fā)狀態(tài)。

Twisted期待著在下一個(gè)十年里成為你遨游互聯(lián)網(wǎng)的引擎


我們?yōu)槭裁匆獙W(xué) Python?地產(chǎn)大佬潘石屹曾在微博回答過:在這信息時(shí)代想要讓機(jī)器為人工作,就必須學(xué)習(xí)機(jī)器的語言,而Python就是進(jìn)化后比較好的計(jì)算機(jī)語言。總的來講,Python絕對是近年最火的編程語言。TIOBE于2020年3月公布的編程語言排行榜,Python 位居前三。追其根本,就是因?yàn)镻ython廣泛的應(yīng)用領(lǐng)域。下面我們一起來看看“Python語言的應(yīng)用領(lǐng)域主要有哪些”。

應(yīng)用領(lǐng)域1:人工智能

Python語言是目前公認(rèn)學(xué)習(xí)人工智能的基礎(chǔ)語言,很多開源的機(jī)器學(xué)習(xí)項(xiàng)目都是基于 Python語言編寫的,例如:用于身份認(rèn)證的人臉識(shí)別系統(tǒng)。這因?yàn)槟_本語言寫起來簡單容易,用Python只是寫邏輯,幾行代碼就出來了。

應(yīng)用領(lǐng)域2:云計(jì)算

云計(jì)算是未來發(fā)展的一大趨勢,Python是為云計(jì)算服務(wù)的。很多常用的云計(jì)算框架都有Python的身影,例如:OpenStackPython的火爆,很大一部分就是因?yàn)榻鼛啄甏罅吭朴?jì)算項(xiàng)目的應(yīng)用。

應(yīng)用領(lǐng)域3:Web開發(fā)

在Web開發(fā)領(lǐng)域,Python擁有很多免費(fèi)數(shù)據(jù)函數(shù)庫、免費(fèi)網(wǎng)頁模板系統(tǒng),以及與Web服務(wù)器進(jìn)行交互的庫,可以搭建Web框架,快速實(shí)現(xiàn)Web開發(fā)。例如:我們經(jīng)常使用的豆瓣網(wǎng)、知乎這些平臺(tái)都是用Python開發(fā)的。

應(yīng)用領(lǐng)域4:爬蟲技術(shù)

在爬蟲領(lǐng)域Python幾乎是霸主地位,Python可以將網(wǎng)絡(luò)一切數(shù)據(jù)作為資源,通過自動(dòng)化程序進(jìn)行有針對性的數(shù)據(jù)采集以及處理。用python來寫爬蟲,會(huì)比用其他編程語言寫要簡單的多,因?yàn)閜ython本身就是一門簡潔的語言。

應(yīng)用領(lǐng)域5:網(wǎng)絡(luò)游戲開發(fā)

在網(wǎng)絡(luò)游戲開發(fā)方面,Python 可以用更少的代碼描述游戲業(yè)務(wù)邏輯。例如:我們平常玩的游戲陰陽師就是用 Python 編寫的。

應(yīng)用領(lǐng)域6:數(shù)據(jù)分析

在數(shù)據(jù)分析方面,Python 是金融分析、量化交易領(lǐng)域里用的最多的語言,平常工作中復(fù)雜的Excel報(bào)表處理也可以用Python來完成對數(shù)據(jù)分析師來講,Python語言是數(shù)據(jù)分析的利器。

相信隨著 Python 的不斷的發(fā)展和影響力的擴(kuò)大, Python語言的應(yīng)用領(lǐng)域會(huì)越來越多。對于 IT 從業(yè)者來說,Python 開發(fā)職位多,工資高,晉升快。而對于非 IT 從業(yè)者來說,學(xué)會(huì) Python 可應(yīng)用到實(shí)際工作中,提高工作效率,進(jìn)而提升自己的綜合競爭力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容