Python源碼剖析筆記3-Python執(zhí)行原理初探

Python源碼剖析筆記3-Python執(zhí)行原理初探

之前寫了幾篇源碼剖析筆記纯路,然而慢慢覺得沒有從一個宏觀的角度理解python執(zhí)行原理的話姥份,從底向上分析未免太容易讓人疑惑凡伊,不如先從宏觀上對python執(zhí)行原理有了一個基本了解可帽,再慢慢探究細節(jié)钾军,這樣也許會好很多鳄袍。這也是最近這么久沒有更新了筆記了,一直在看源碼剖析書籍和源碼吏恭,希望能夠從一個宏觀層面理清python執(zhí)行原理拗小。人說讀書從薄讀厚,再從厚讀薄方是理解了真意樱哼,希望能夠達到這個境地吧哀九,加了個油。

1 Python運行環(huán)境初始化

在看怎么執(zhí)行之前搅幅,先要簡單的說明一下python的運行時環(huán)境初始化阅束。python中有一個解釋器狀態(tài)對象PyInterpreterState用于模擬進程(后面簡稱進程對象),另外有一個線程狀態(tài)對象PyThreadState模擬線程(后面簡稱線程對象)茄唐。python中的PyInterpreterState結(jié)構(gòu)通過一個鏈表鏈接起來息裸,用于模擬操作系統(tǒng)多進程。進程對象中有一個指針指向線程集合沪编,線程對象則有一個指針指向其對應(yīng)的進程對象呼盆,這樣線程和進程就關(guān)聯(lián)了起來。當(dāng)然漾抬,還少不了一個當(dāng)前運行線程對象_PyThreadState_Current用來維護當(dāng)前運行的線程宿亡。

1.1 進程線程初始化

python中調(diào)用PyInitialize()函數(shù)來完成運行環(huán)境初始化。在初始化函數(shù)中纳令,會創(chuàng)建進程對象interp以及線程對象并在進程對象和線程對象建立關(guān)聯(lián)挽荠,并設(shè)置當(dāng)前運行線程對象為剛創(chuàng)建的線程對象克胳。接下來是類型系統(tǒng)初始化,包括int圈匆,str漠另,bool,list等類型初始化跃赚,這里留到后面再慢慢分析笆搓。然后,就是另外一個大頭纬傲,那就是系統(tǒng)模塊初始化满败。進程對象interp中有一個modules變量用于維護所有的模塊對象,modules變量為字典對象,其中維護(name, module)對應(yīng)關(guān)系叹括,在python中對應(yīng)著sys.modules算墨。

1.2 模塊初始化

系統(tǒng)模塊初始化過程會初始化 __builtin__, sys, __main__, site等模塊。在python中汁雷,模塊對象是以PyModuleObject結(jié)構(gòu)體存在的净嘀,除了通用的對象頭部,其中就只有一個字典字段md_dict侠讯。模塊對象中的md_dict字段存儲的內(nèi)容是我們很熟悉的,比如__name__, __doc__等屬性挖藏,以及模塊中的方法等。

__builtin__模塊初始化中厢漩,md_dict中存儲的內(nèi)容就包括內(nèi)置函數(shù)以及系統(tǒng)類型對象膜眠,如len,dir,getattr等函數(shù)以及int,str,list等類型對象。正因為如此溜嗜,我們才能在代碼中直接用len函數(shù)柴底,因為根據(jù)LEGB規(guī)則,我們能夠在__builtin__模塊中找到len這個符號粱胜。幾乎同樣的過程創(chuàng)建sys模塊以及__main__模塊柄驻。創(chuàng)建完成后,進程對象interp->builtins會被設(shè)置為__builtin__模塊的md_dict字段焙压,即模塊對象中的那個字典字段鸿脓。而interp->sysdict則是被設(shè)置為sys模塊的md_dict字段。

sys模塊初始化后涯曲,其中包括前面提到過的modules以及path,version,stdin,stdout,maxint等屬性野哭,exit,getrefcount,_getframe等函數(shù)。注意這里是設(shè)置了基本的sys.path(即python安裝目錄的lib路徑等)幻件,第三方模塊的路徑是在site模塊初始化的時候添加的拨黔。

需要說明的是,__main__模塊是個特殊的模塊绰沥,在我們寫第一個python程序時篱蝇,其中的__name__ == "__main__"中的__main__指的就是這個模塊名字贺待。當(dāng)我們用python xxx.py運行python程序時,該源文件就可以當(dāng)作是名為__main__的模塊了零截,而如果是通過其他模塊導(dǎo)入麸塞,則其名字就是源文件本身的名字,至于為什么涧衙,這個在后面運行一個python程序的例子中會詳細說明哪工。其中還有一點要說明的是,在創(chuàng)建__main__模塊的時候弧哎,會在模塊的字典中插入("__builtins__", __builtin__ module)對應(yīng)關(guān)系雁比。在后面可以看到這個模塊特別重要,因為在運行時棧幀對象PyFrameObject的f_buitins字段就會被設(shè)置為__builtin__模塊撤嫩,而棧幀對象的locals和globals字段初始會被設(shè)置為__main__模塊的字典章贞。

另外,site模塊初始化主要用來初始化python第三方模塊搜索路徑非洲,我們經(jīng)常用的sys.path就是這個模塊設(shè)置的了。它不僅將site-packages路徑加到sys.path中蜕径,還會把site-packages目錄下面的.pth文件中的所有路徑加入到sys.path中两踏。

下面是一些驗證代碼,可以看到sys.modules中果然有了__builtin__, sys, __main__等模塊兜喻。此外梦染,系統(tǒng)的類型對象都已經(jīng)位于__builtin__模塊字典中。

In [13]: import sys

In [14]: sys.modules['__builtin__'].__dict__['int']
Out[14]: int

In [15]: sys.modules['__builtin__'].__dict__['len']
Out[15]: <function len>

In [16]: sys.modules['__builtin__'].__dict__['__name__']
Out[16]: '__builtin__'

In [17]: sys.modules['__builtin__'].__dict__['__doc__']
Out[17]: "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices."

In [18]: sys.modules['sys']
Out[18]: <module 'sys' (built-in)>

In [19]: sys.modules['__main__']
Out[19]: <module '__main__' (built-in)>

好了朴皆,基本工作已經(jīng)準備妥當(dāng)帕识,接下來可以運行python程序了。有兩種方式遂铡,一種是在命令行下面的交互肮疗,另外一種是以python xxx.py的方式運行。在說明這兩種方式前扒接,需要先介紹下python程序運行相關(guān)的幾個結(jié)構(gòu)伪货。

1.3 Python運行相關(guān)數(shù)據(jù)結(jié)構(gòu)

python運行相關(guān)數(shù)據(jù)結(jié)構(gòu)主要由PyCodeObject,PyFrameObject以及PyFunctionObject钾怔。其中PyCodeObject是python字節(jié)碼的存儲結(jié)構(gòu)碱呼,編譯后的pyc文件就是以PyCodeObject結(jié)構(gòu)序列化后存儲的,運行時加載并反序列化為PyCodeObject對象宗侦。PyFrameObject是對棧幀的模擬愚臀,當(dāng)進入到一個新的函數(shù)時,都會有PyFrameObject對象用于模擬棧幀操作矾利。PyFunctionObject則是函數(shù)對象姑裂,一個函數(shù)對應(yīng)一個PyCodeObject,在執(zhí)行def test():語句的時候會創(chuàng)建PyFunctionObject對象馋袜。可以這樣認為炭分,PyCodeObject是一種靜態(tài)的結(jié)構(gòu)桃焕,python源文件確定,那么編譯后的PyCodeObject對象也是不變的捧毛;而PyFrameObject和PyFunctionObject是動態(tài)結(jié)構(gòu)观堂,其中的內(nèi)容會在運行時動態(tài)變化。

PyCodeObject對象

python程序文件在執(zhí)行前需要編譯成PyCodeObject對象呀忧,每一個CodeBlock都會是一個PyCodeObject對象师痕,在Python中,類而账,函數(shù)胰坟,模塊都是一個Code Block,也就是說編譯后都有一個單獨的PyCodeObject對象泞辐,因此笔横,一個python文件編譯后可能會有多個PyCodeObject對象,比如下面的示例程序編譯后就會存在2個PyCodeObject對象咐吼,一個對應(yīng)test.py整個文件吹缔,一個對應(yīng)函數(shù)test。關(guān)于PyCodeObject對象的解析锯茄,可以參見我之前的文章Python pyc格式解析厢塘,這里就不贅述了。

#示例代碼test.py
def test():
    print "hello world"

if __name__ == "__main__":
    test()

PyFrameObject對象

python程序的字節(jié)碼指令以及一些靜態(tài)信息比如常量等都存儲在PyCodeObject中肌幽,運行時顯然不可能只是操作PyCodeObject對象晚碾,因為有很多內(nèi)容是運行時動態(tài)改變的,比如下面這個代碼test2.py,雖然1和2處的字節(jié)碼指令相同喂急,但是它們執(zhí)行結(jié)果顯然是不同的格嘁,這些信息顯然不能在PyCodeObject中存儲,這些信息其實需要通過PyFrameObject也就是棧幀對象來獲取廊移。PyFrameObject對象中有l(wèi)ocals讥蔽,globals,builtins三個字段對應(yīng)local画机,global冶伞,builtin三個名字空間,即我們常說的LGB規(guī)則步氏,當(dāng)然加上閉包响禽,就是LEGB規(guī)則。一個模塊對應(yīng)的文件定義一個global作用域,一個函數(shù)定義一個local作用域芋类,python自身定義了一個頂級作用域builtin作用域隆嗅,這三個作用域分別對應(yīng)PyFrameObject對象的三個字段,這樣就可以找到對應(yīng)的名字引用侯繁。比如test2.py中的1處的i引用的是函數(shù)test的局部變量i胖喳,對應(yīng)內(nèi)容是字符串“hello world”,而2處的i引用的是模塊的local作用域的名字i贮竟,對應(yīng)內(nèi)容是整數(shù)123(注意模塊的local作用域和global作用域是一樣的)丽焊。需要注意的是,函數(shù)中局部變量的訪問并不需要去訪問locals名字空間咕别,因為函數(shù)的局部變量總是不變的技健,在編譯時就能確定局部變量使用的內(nèi)存位置。

#示例代碼test2.py
i = 123                                                                                                                                                       

def test():
  i = 'hello world'
  print i #1

test()
print i #2

PyFunctionObject對象

PyFunctionObject是函數(shù)對象惰拱,在創(chuàng)建函數(shù)的指令MAKE_FUNCTION中構(gòu)建雌贱。PyFunctionObject中有個func_code字段指向該函數(shù)對應(yīng)的PyCodeObject對象,另外還有func_globals指向global名字空間偿短,注意到這里并沒有使用local名字空間欣孤。調(diào)用函數(shù)時,會創(chuàng)建新的棧幀對象PyFrameObject來執(zhí)行函數(shù)昔逗,函數(shù)調(diào)用關(guān)系通過棧幀對象PyFrameObject中的f_back字段進行關(guān)聯(lián)降传。最終執(zhí)行函數(shù)調(diào)用時,PyFunctionObject對象的影響已經(jīng)消失纤子,真正起作用的是PyFunctionObject的PyCodeObject對象和global名字空間,因為在創(chuàng)建函數(shù)棧幀時會將這兩個參數(shù)傳給PyFrameObject對象款票。

1.4 Python程序運行過程淺析

說完幾個基本對象控硼,現(xiàn)在回到之前的話題,開始準備執(zhí)行python程序艾少。兩種方式交互式和直接python xxx.py雖然有所不同卡乾,但最終歸于一處,就是啟動虛擬機執(zhí)行python字節(jié)碼缚够。這里以python xxx.py方式為例幔妨,在運行python程序之前,需要對源文件編譯成字節(jié)碼谍椅,創(chuàng)建PyCodeObject對象误堡。這個是通過PyAST_Compile函數(shù)實現(xiàn)的,至于具體編譯流程雏吭,這就要參看《編譯原理》那本龍書了锁施,這里暫時當(dāng)做黑盒好了,因為單就編譯這部分而言,一時半會也說不清楚(好吧悉抵,其實是我也沒有學(xué)好編譯原理)肩狂。編譯后得到PyCodeObject對象,然后調(diào)用PyEval_EvalCode(co, globals, locals)函數(shù)創(chuàng)建PyFrameObject對象并執(zhí)行字節(jié)碼了姥饰。注意到參數(shù)里面的co是PyCodeObject對象傻谁,而由于運行PyEval_EvalCode時創(chuàng)建的棧幀對象是Python創(chuàng)建的第一個PyFrameObject對象,所以f_back為NULL列粪,而且它的globals和locals就是__main__模塊的字典對象审磁。如果我們不是直接運行,而是導(dǎo)入一個模塊的話篱竭,則還會將python源碼編譯后得到的PyCodeObject對象保存到pyc文件中力图,下次加載模塊時如果這個模塊沒有改動過就可以直接從pyc文件中讀取內(nèi)容而不需要再次編譯了。

執(zhí)行字節(jié)碼的過程就是模擬CPU執(zhí)行指令的過程一樣掺逼,先指向PyFrameObject的f_code字段對應(yīng)的PyCodeObject對象的co_code字段吃媒,這就是字節(jié)碼存儲的位置,然后取出第一條指令吕喘,接著第二條指令...依次執(zhí)行完所有的指令赘那。python中指令長度為1個字節(jié)或者3個字節(jié),其中無參數(shù)的指令長度是1個字節(jié)氯质,有參數(shù)的指令長度是3個字節(jié)(指令1字節(jié)+參數(shù)2字節(jié))募舟。

python虛擬機的進程,線程闻察,棧幀對象等關(guān)系如下圖所示:

py.png

2 Python程序運行實例說明

程序猿學(xué)習(xí)一門新的語言往往都是從hello world開始的拱礁,一來就跟世界打個招呼,因為接下來就要去面對程序語言未知的世界了辕漂。我學(xué)習(xí)python也是從這里開始的呢灶,只是以前并不去深究它的執(zhí)行原理,這回是逃不過去了钉嘹⊙炷耍看看下面的栗子。

#示例代碼test3.py
i = 1
s = 'hello world'

def test():
    k = 5
    print k
    print s

if __name__ == "__main__":
    test()

這個例子代碼不多跋涣,不過也涉及到python運行原理的方方面面(除了類機制那一塊外缨睡,類機制那一塊還沒有理清楚,先不理會)陈辱。那么按照之前部分說的奖年,執(zhí)行python test3.py的時候,會先初始化python進程和線程沛贪,然后初始化系統(tǒng)模塊以及類型系統(tǒng)等拾并,然后運行python程序test3.py揍堰。每次運行python程序都是開啟一個python虛擬機,由于是直接運行嗅义,需要先編譯為字節(jié)碼格式屏歹,得到PyCodeObject對象,然后從字節(jié)碼對象的第一條指令開始執(zhí)行之碗。因為是直接運行蝙眶,所以PyCodeObject也就沒有序列化到pyc文件保存了。下面可以看下test3.py的PyCodeObject褪那,使用python的dis模塊可以看到字節(jié)碼指令幽纷。

In [1]: source = open('test3.py').read()

In [2]: co = compile(source, 'test3.py', 'exec')

In [3]: co.co_consts
Out[3]: 
(1,
 'hello world',
 <code object test at 0x1108eaaf8, file "run.py", line 4>,
 '__main__',
 None)

In [4]: co.co_names
Out[4]: ('i', 's', 'test', '__name__')

In [5]: dis.dis(co) ##模塊本身的字節(jié)碼,下面說的整數(shù)博敬,字符串等都是指python中的對象友浸,對應(yīng)PyIntObject,PyStringObject等偏窝。
  1           0 LOAD_CONST               0 (1) # 加載常量表中的第0個常量也就是整數(shù)1到棧中收恢。
              3 STORE_NAME               0 (i) # 獲取變量名i,出棧剛剛加載的整數(shù)1祭往,然后存儲變量名和整數(shù)1到f->f_locals中伦意,這個字段對應(yīng)著查找名字時的local名字空間。

  2           6 LOAD_CONST               1 ('hello world') 

              9 STORE_NAME               1 (s)  #同理硼补,獲取變量名s驮肉,出棧剛剛加載的字符串hello world,并存儲變量名和字符串hello world的對應(yīng)關(guān)系到local名字空間已骇。

  4          12 LOAD_CONST               2 (<code object test at 0xb744bd10, file "test3.py", line 4>)
             15 MAKE_FUNCTION            0   #出棧剛剛?cè)霔5暮瘮?shù)test的PyCodeObject對象离钝,以code object和PyFrameObject的f_globals為參數(shù)創(chuàng)建函數(shù)對象PyFunctionObject并入棧
             18 STORE_NAME               2 (test)  #獲取變量test,并出棧剛?cè)霔5腜yFunctionObject對象褪储,并存儲到local名字空間卵渴。

  9          21 LOAD_NAME                3 (__name__) ##LOAD_NAME會先依次搜索local,global乱豆,builtin名字空間奖恰,當(dāng)然我們這里是在local名字空間能找到__name__吊趾。
             24 LOAD_CONST               3 ('__main__')
             27 COMPARE_OP               2 (==)  ##比較指令
             30 JUMP_IF_FALSE           11 (to 44) ##如果不相等則直接跳轉(zhuǎn)到44對應(yīng)的指令處宛裕,也就是下面的POP_TOP。因為在COMPARE_OP指令中论泛,會設(shè)置棧頂為比較的結(jié)果揩尸,所以需要出棧這個比較結(jié)果。當(dāng)然我們這里是相等屁奏,所以接著往下執(zhí)行33處的指令岩榆,也是POP_TOP。
             33 POP_TOP             

 10          34 LOAD_NAME                2 (test) ##加載函數(shù)對象
             37 CALL_FUNCTION            0  ##調(diào)用函數(shù)
             40 POP_TOP                     ##出棧函數(shù)返回值
             41 JUMP_FORWARD             1 (to 45) ##前進1步,注意是下一條指令地址+1勇边,也就是44+1=45
        >>   44 POP_TOP             
        >>   45 LOAD_CONST               4 (None) 
             48 RETURN_VALUE     #返回None


In [6]: dis.dis(co.co_consts[2])  ##查看函數(shù)test的字節(jié)碼
  5           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (k) #STORE_FAST與STORE_NAME不同犹撒,它是存儲到PyFrameObject的f_localsplus中,不是local名字空間粒褒。

  6           6 LOAD_FAST                0 (k) #相對應(yīng)的识颊,LOAD_FAST是從f_localsplus取值
              9 PRINT_ITEM          
             10 PRINT_NEWLINE         #打印輸出 

  7          11 LOAD_GLOBAL              0 (s) #因為函數(shù)沒有使用local名字空間,所以奕坟,這里不是LOAD_NAME,而是LOAD_GLOBAL祥款,不要被名字迷惑,它實際上會依次搜索global月杉,builtin名字空間刃跛。
             14 PRINT_ITEM          
             15 PRINT_NEWLINE       
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

按照我們前面的分析,test3.py這個文件編譯后其實對應(yīng)2個PyCodeObject苛萎,一個是本身test3.py這個模塊整體的PyCodeObject桨昙,另外一個則是函數(shù)test對應(yīng)的PyCodeObject。根據(jù)PyCodeObject的結(jié)構(gòu)首懈,我們可以知道test3.py字節(jié)碼中常量co_consts有5個绊率,分別是整數(shù)1,字符串‘hello world'究履,函數(shù)test對應(yīng)的PyCodeObject對象滤否,字符串__main__,以及模塊返回值None對象最仑。恩藐俺,從這里可以發(fā)現(xiàn),其實模塊也是有返回值的泥彤。我們同樣可以用dis模塊查看函數(shù)test的字節(jié)碼欲芹。

關(guān)于字節(jié)碼指令,代碼中做了解析吟吝。需要注意到函數(shù)中局部變量如k的取值用的是LOAD_FAST菱父,即直接從PyFrameObject的f_localsplus字段取,而不是LOAD_NAME那樣依次從local剑逃,global以及builtin查找浙宜,這是函數(shù)的特性決定的。函數(shù)的運行時棧也是位于f_localsplus對應(yīng)的那片內(nèi)存中蛹磺,只是前面一部分用于存儲函數(shù)參數(shù)和局部變量粟瞬,而后面那部分才是運行時棧使用,這樣邏輯上運行時棧和函數(shù)參數(shù)以及局部變量是分離的萤捆,雖然物理上它們是連在一起的裙品。需要注意的是俗批,python中使用了預(yù)測指令機制,比如COMPARE_OP經(jīng)常跟JUMP_IF_FALSE或JUMP_IF_TRUE成對出現(xiàn)市怎,所以如果COMPARE_OP的下一條指令正好是JUNP_IF_FALSE岁忘,則可以直接跳轉(zhuǎn)到對應(yīng)代碼處執(zhí)行,提高一定效率区匠。

此外臭觉,還要知道在運行test3.py的時候,模塊的test3.py棧幀對象中的f_locals和f_globals的值是一樣的辱志,都是__main__模塊的字典蝠筑。在test3.py的代碼后面加上如下代碼可以驗證這個猜想。

 ... #test3.py的代碼
 
if __name__ == "__main__":
    test()
    print locals() == sys.modules['__main__'].__dict__ # True
    print globals() == sys.modules['__main__'].__dict__ # True
    print globals() == locals() # True

正式因為如此揩懒,所以python中函數(shù)定義順序是無關(guān)的什乙,不需要跟C語言那樣在調(diào)用函數(shù)前先聲明函數(shù)。比如下面test4.py是完全正常的代碼已球,函數(shù)定義順序不影響函數(shù)調(diào)用臣镣,因為在執(zhí)行def語句的時候,會執(zhí)行MAKE_FUNCTION指令將函數(shù)對象加入到local名字空間智亮,而local和global此時對應(yīng)的是同一個字典忆某,所以也相當(dāng)于加入了global名字空間,從而在運行函數(shù)g的時候是可以找到函數(shù)f的阔蛉。另外也可以注意到弃舒,函數(shù)聲明和實現(xiàn)其實是分離的,聲明的字節(jié)碼指令在模塊的PyCodeObject中執(zhí)行状原,而實現(xiàn)的字節(jié)碼指令則是在函數(shù)自己的PyCodeObject中聋呢。

#test4.py
def g():                                                                                                                                                     
  print 'function g'
  f() 

def f():
  print 'function f'

g()
~      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市颠区,隨后出現(xiàn)的幾起案子削锰,更是在濱河造成了極大的恐慌,老刑警劉巖毕莱,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件器贩,死亡現(xiàn)場離奇詭異,居然都是意外死亡朋截,警方通過查閱死者的電腦和手機蛹稍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來质和,“玉大人稳摄,你說我怎么就攤上這事稚字∷撬蓿” “怎么了厦酬?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瘫想。 經(jīng)常有香客問我仗阅,道長,這世上最難降的妖魔是什么国夜? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任减噪,我火速辦了婚禮,結(jié)果婚禮上车吹,老公的妹妹穿的比我還像新娘筹裕。我一直安慰自己,他們只是感情好窄驹,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布朝卒。 她就那樣靜靜地躺著,像睡著了一般乐埠。 火紅的嫁衣襯著肌膚如雪抗斤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天丈咐,我揣著相機與錄音瑞眼,去河邊找鬼。 笑死棵逊,一個胖子當(dāng)著我的面吹牛伤疙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辆影,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼掩浙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了秸歧?” 一聲冷哼從身側(cè)響起厨姚,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎键菱,沒想到半個月后谬墙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡经备,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年拭抬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侵蒙。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡造虎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纷闺,到底是詐尸還是另有隱情算凿,我是刑警寧澤份蝴,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站氓轰,受9級特大地震影響婚夫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜署鸡,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一案糙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧靴庆,春花似錦时捌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至端礼,卻和暖如春禽笑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛤奥。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工佳镜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凡桥。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓蟀伸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缅刽。 傳聞我的和親對象是個殘疾皇子啊掏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Python的函數(shù)機制是很重要的部分,很多時候用python寫腳本衰猛,就是幾個函數(shù)簡單解決問題迟蜜,不需要像java那樣...
    __七把刀__閱讀 5,637評論 0 12
  • 一、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運行的地址不確定 關(guān)于...
    SeanCST閱讀 7,779評論 0 27
  • 1. 簡單的例子 先從一個簡單的例子說起啡省,包含了兩個文件 foo.py 和 demo.py 執(zhí)行這個程序pytho...
    jiangmo閱讀 1,662評論 0 5
  • python學(xué)習(xí)筆記 聲明:學(xué)習(xí)筆記主要是根據(jù)廖雪峰官方網(wǎng)站python學(xué)習(xí)學(xué)習(xí)的娜睛,另外根據(jù)自己平時的積累進行修正...
    renyangfar閱讀 3,020評論 0 10
  • 要點: 函數(shù)式編程:注意不是“函數(shù)編程”,多了一個“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍钬远谩傩浴?..
    victorsungo閱讀 1,468評論 0 6