溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python執(zhí)行原理的分析

發(fā)布時(shí)間:2020-08-01 09:14:36 來(lái)源:億速云 閱讀:119 作者:清晨 欄目:編程語(yǔ)言

這篇文章主要介紹Python執(zhí)行原理的分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

一、Python運(yùn)行環(huán)境初始化

在看怎么執(zhí)行之前,先要簡(jiǎn)單的說(shuō)明一下python的運(yùn)行時(shí)環(huán)境初始化。python中有一個(gè)解釋器狀態(tài)對(duì)象PyInterpreterState用于模擬進(jìn)程(后面簡(jiǎn)稱進(jìn)程對(duì)象),另外有一個(gè)線程狀態(tài)對(duì)象PyThreadState模擬線程(后面簡(jiǎn)稱線程對(duì)象)。

python中的PyInterpreterState結(jié)構(gòu)通過(guò)一個(gè)鏈表鏈接起來(lái),用于模擬操作系統(tǒng)多進(jìn)程。進(jìn)程對(duì)象中有一個(gè)指針指向線程集合,線程對(duì)象則有一個(gè)指針指向其對(duì)應(yīng)的進(jìn)程對(duì)象,這樣線程和進(jìn)程就關(guān)聯(lián)了起來(lái)。當(dāng)然,還少不了一個(gè)當(dāng)前運(yùn)行線程對(duì)象_PyThreadState_Current用來(lái)維護(hù)當(dāng)前運(yùn)行的線程。

1、進(jìn)程線程初始化

python中調(diào)用PyInitialize()函數(shù)來(lái)完成運(yùn)行環(huán)境初始化。在初始化函數(shù)中,會(huì)創(chuàng)建進(jìn)程對(duì)象interp以及線程對(duì)象并在進(jìn)程對(duì)象和線程對(duì)象建立關(guān)聯(lián),并設(shè)置當(dāng)前運(yùn)行線程對(duì)象為剛創(chuàng)建的線程對(duì)象。接下來(lái)是類型系統(tǒng)初始化,包括int,str,bool,list等類型初始化,這里留到后面再慢慢分析。然后,就是另外一個(gè)大頭,那就是系統(tǒng)模塊初始化。

進(jìn)程對(duì)象interp中有一個(gè)modules變量用于維護(hù)所有的模塊對(duì)象,modules變量為字典對(duì)象,其中維護(hù)(name, module)對(duì)應(yīng)關(guān)系,在python中對(duì)應(yīng)著sys.modules。

2、模塊初始化

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

在__builtin__模塊初始化中,md_dict中存儲(chǔ)的內(nèi)容就包括內(nèi)置函數(shù)以及系統(tǒng)類型對(duì)象,如len,dir,getattr等函數(shù)以及int,str,list等類型對(duì)象。正因?yàn)槿绱?,我們才能在代碼中直接用len函數(shù),因?yàn)楦鶕?jù)LEGB規(guī)則,我們能夠在__builtin__模塊中找到len這個(gè)符號(hào)。幾乎同樣的過(guò)程創(chuàng)建sys模塊以及__main__模塊。創(chuàng)建完成后,進(jìn)程對(duì)象interp->builtins會(huì)被設(shè)置為__builtin__模塊的md_dict字段,即模塊對(duì)象中的那個(gè)字典字段。而interp->sysdict則是被設(shè)置為sys模塊的md_dict字段。

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

需要說(shuō)明的是,__main__模塊是個(gè)特殊的模塊,在我們寫第一個(gè)python程序時(shí),其中的__name__ == "__main__"中的__main__指的就是這個(gè)模塊名字。當(dāng)我們用python xxx.py運(yùn)行python程序時(shí),該源文件就可以當(dāng)作是名為__main__的模塊了,而如果是通過(guò)其他模塊導(dǎo)入,則其名字就是源文件本身的名字,至于為什么,這個(gè)在后面運(yùn)行一個(gè)python程序的例子中會(huì)詳細(xì)說(shuō)明。

其中還有一點(diǎn)要說(shuō)明的是,在創(chuàng)建__main__模塊的時(shí)候,會(huì)在模塊的字典中插入("__builtins__", __builtin__ module)對(duì)應(yīng)關(guān)系。在后面可以看到這個(gè)模塊特別重要,因?yàn)樵谶\(yùn)行時(shí)棧幀對(duì)象PyFrameObject的f_buitins字段就會(huì)被設(shè)置為__builtin__模塊,而棧幀對(duì)象的locals和globals字段初始會(huì)被設(shè)置為__main__模塊的字典。

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

下面是一些驗(yàn)證代碼,可以看到sys.modules中果然有了__builtin__, sys, __main__等模塊。此外,系統(tǒng)的類型對(duì)象都已經(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)準(zhǔn)備妥當(dāng),接下來(lái)可以運(yùn)行python程序了。有兩種方式,一種是在命令行下面的交互,另外一種是以python xxx.py的方式運(yùn)行。在說(shuō)明這兩種方式前,需要先介紹下python程序運(yùn)行相關(guān)的幾個(gè)結(jié)構(gòu)。

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

python運(yùn)行相關(guān)數(shù)據(jù)結(jié)構(gòu)主要由PyCodeObject,PyFrameObject以及PyFunctionObject。其中PyCodeObject是python字節(jié)碼的存儲(chǔ)結(jié)構(gòu),編譯后的pyc文件就是以PyCodeObject結(jié)構(gòu)序列化后存儲(chǔ)的,運(yùn)行時(shí)加載并反序列化為PyCodeObject對(duì)象。PyFrameObject是對(duì)棧幀的模擬,當(dāng)進(jìn)入到一個(gè)新的函數(shù)時(shí),都會(huì)有PyFrameObject對(duì)象用于模擬棧幀操作。

PyFunctionObject則是函數(shù)對(duì)象,一個(gè)函數(shù)對(duì)應(yīng)一個(gè)PyCodeObject,在執(zhí)行def test():語(yǔ)句的時(shí)候會(huì)創(chuàng)建PyFunctionObject對(duì)象??梢赃@樣認(rèn)為,PyCodeObject是一種靜態(tài)的結(jié)構(gòu),python源文件確定,那么編譯后的PyCodeObject對(duì)象也是不變的;而PyFrameObject和PyFunctionObject是動(dòng)態(tài)結(jié)構(gòu),其中的內(nèi)容會(huì)在運(yùn)行時(shí)動(dòng)態(tài)變化。

PyCodeObject對(duì)象

python程序文件在執(zhí)行前需要編譯成PyCodeObject對(duì)象,每一個(gè)CodeBlock都會(huì)是一個(gè)PyCodeObject對(duì)象,在Python中,類,函數(shù),模塊都是一個(gè)Code Block,也就是說(shuō)編譯后都有一個(gè)單獨(dú)的PyCodeObject對(duì)象,因此,一個(gè)python文件編譯后可能會(huì)有多個(gè)PyCodeObject對(duì)象,比如下面的示例程序編譯后就會(huì)存在2個(gè)PyCodeObject對(duì)象,一個(gè)對(duì)應(yīng)test.py整個(gè)文件,一個(gè)對(duì)應(yīng)函數(shù)test。

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

if __name__ == "__main__":
    test()

PyFrameObject對(duì)象

python程序的字節(jié)碼指令以及一些靜態(tài)信息比如常量等都存儲(chǔ)在PyCodeObject中,運(yùn)行時(shí)顯然不可能只是操作PyCodeObject對(duì)象,因?yàn)橛泻芏鄡?nèi)容是運(yùn)行時(shí)動(dòng)態(tài)改變的,比如下面這個(gè)代碼test2.py,雖然1和2處的字節(jié)碼指令相同,但是它們執(zhí)行結(jié)果顯然是不同的,這些信息顯然不能在PyCodeObject中存儲(chǔ),這些信息其實(shí)需要通過(guò)PyFrameObject也就是棧幀對(duì)象來(lái)獲取。

PyFrameObject對(duì)象中有l(wèi)ocals,globals,builtins三個(gè)字段對(duì)應(yīng)local,global,builtin三個(gè)名字空間,即我們常說(shuō)的LGB規(guī)則,當(dāng)然加上閉包,就是LEGB規(guī)則。一個(gè)模塊對(duì)應(yīng)的文件定義一個(gè)global作用域,一個(gè)函數(shù)定義一個(gè)local作用域,python自身定義了一個(gè)頂級(jí)作用域builtin作用域,這三個(gè)作用域分別對(duì)應(yīng)PyFrameObject對(duì)象的三個(gè)字段,這樣就可以找到對(duì)應(yīng)的名字引用。

比如test2.py中的1處的i引用的是函數(shù)test的局部變量i,對(duì)應(yīng)內(nèi)容是字符串“hello world”,而2處的i引用的是模塊的local作用域的名字i,對(duì)應(yīng)內(nèi)容是整數(shù)123(注意模塊的local作用域和global作用域是一樣的)。需要注意的是,函數(shù)中局部變量的訪問(wèn)并不需要去訪問(wèn)locals名字空間,因?yàn)楹瘮?shù)的局部變量總是不變的,在編譯時(shí)就能確定局部變量使用的內(nèi)存位置。

#示例代碼test2.py
i = 123      
def test():
  i = 'hello world'
  print i #1

test()
print i #2

PyFunctionObject對(duì)象

PyFunctionObject是函數(shù)對(duì)象,在創(chuàng)建函數(shù)的指令MAKE_FUNCTION中構(gòu)建。PyFunctionObject中有個(gè)func_code字段指向該函數(shù)對(duì)應(yīng)的PyCodeObject對(duì)象,另外還有func_globals指向global名字空間,注意到這里并沒(méi)有使用local名字空間。調(diào)用函數(shù)時(shí),會(huì)創(chuàng)建新的棧幀對(duì)象PyFrameObject來(lái)執(zhí)行函數(shù),函數(shù)調(diào)用關(guān)系通過(guò)棧幀對(duì)象PyFrameObject中的f_back字段進(jìn)行關(guān)聯(lián)。

最終執(zhí)行函數(shù)調(diào)用時(shí),PyFunctionObject對(duì)象的影響已經(jīng)消失,真正起作用的是PyFunctionObject的PyCodeObject對(duì)象和global名字空間,因?yàn)樵趧?chuàng)建函數(shù)棧幀時(shí)會(huì)將這兩個(gè)參數(shù)傳給PyFrameObject對(duì)象。

4、Python程序運(yùn)行過(guò)程淺析

說(shuō)完幾個(gè)基本對(duì)象,現(xiàn)在回到之前的話題,開始準(zhǔn)備執(zhí)行python程序。兩種方式交互式和直接python xxx.py雖然有所不同,但最終歸于一處,就是啟動(dòng)虛擬機(jī)執(zhí)行python字節(jié)碼。這里以python xxx.py方式為例,在運(yùn)行python程序之前,需要對(duì)源文件編譯成字節(jié)碼,創(chuàng)建PyCodeObject對(duì)象。這個(gè)是通過(guò)PyAST_Compile函數(shù)實(shí)現(xiàn)的,至于具體編譯流程,這就要參看《編譯原理》那本書了,這里暫時(shí)當(dāng)做黑盒好了,因?yàn)閱尉途幾g這部分而言,一時(shí)半會(huì)也說(shuō)不清楚(好吧,其實(shí)是我也沒(méi)有學(xué)好編譯原理)。

編譯后得到PyCodeObject對(duì)象,然后調(diào)用PyEval_EvalCode(co, globals, locals)函數(shù)創(chuàng)建PyFrameObject對(duì)象并執(zhí)行字節(jié)碼了。注意到參數(shù)里面的co是PyCodeObject對(duì)象,而由于運(yùn)行PyEval_EvalCode時(shí)創(chuàng)建的棧幀對(duì)象是Python創(chuàng)建的第一個(gè)PyFrameObject對(duì)象,所以f_back為NULL,而且它的globals和locals就是__main__模塊的字典對(duì)象。如果我們不是直接運(yùn)行,而是導(dǎo)入一個(gè)模塊的話,則還會(huì)將python源碼編譯后得到的PyCodeObject對(duì)象保存到pyc文件中,下次加載模塊時(shí)如果這個(gè)模塊沒(méi)有改動(dòng)過(guò)就可以直接從pyc文件中讀取內(nèi)容而不需要再次編譯了。

執(zhí)行字節(jié)碼的過(guò)程就是模擬CPU執(zhí)行指令的過(guò)程一樣,先指向PyFrameObject的f_code字段對(duì)應(yīng)的PyCodeObject對(duì)象的co_code字段,這就是字節(jié)碼存儲(chǔ)的位置,然后取出第一條指令,接著第二條指令...依次執(zhí)行完所有的指令。python中指令長(zhǎng)度為1個(gè)字節(jié)或者3個(gè)字節(jié),其中無(wú)參數(shù)的指令長(zhǎng)度是1個(gè)字節(jié),有參數(shù)的指令長(zhǎng)度是3個(gè)字節(jié)(指令1字節(jié)+參數(shù)2字節(jié))。

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

Python執(zhí)行原理的分析

二、Python程序運(yùn)行實(shí)例說(shuō)明

程序猿學(xué)習(xí)一門新的語(yǔ)言往往都是從hello world開始的,一來(lái)就跟世界打個(gè)招呼,因?yàn)榻酉聛?lái)就要去面對(duì)程序語(yǔ)言未知的世界了。我學(xué)習(xí)python也是從這里開始的,只是以前并不去深究它的執(zhí)行原理,這回是逃不過(guò)去了??纯聪旅娴睦踝?。

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

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

if __name__ == "__main__":
    test()

這個(gè)例子代碼不多,不過(guò)也涉及到python運(yùn)行原理的方方面面(除了類機(jī)制那一塊外,類機(jī)制那一塊還沒(méi)有理清楚,先不理會(huì))。那么按照之前部分說(shuō)的,執(zhí)行python test3.py的時(shí)候,會(huì)先初始化python進(jìn)程和線程,然后初始化系統(tǒng)模塊以及類型系統(tǒng)等,然后運(yùn)行python程序test3.py。

每次運(yùn)行python程序都是開啟一個(gè)python虛擬機(jī),由于是直接運(yùn)行,需要先編譯為字節(jié)碼格式,得到PyCodeObject對(duì)象,然后從字節(jié)碼對(duì)象的第一條指令開始執(zhí)行。因?yàn)槭侵苯舆\(yùn)行,所以PyCodeObject也就沒(méi)有序列化到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é)碼,下面說(shuō)的整數(shù),字符串等都是指python中的對(duì)象,對(duì)應(yīng)PyIntObject,PyStringObject等。
  1           0 LOAD_CONST               0 (1) # 加載常量表中的第0個(gè)常量也就是整數(shù)1到棧中。
              3 STORE_NAME               0 (i) # 獲取變量名i,出棧剛剛加載的整數(shù)1,然后存儲(chǔ)變量名和整數(shù)1到f->f_locals中,這個(gè)字段對(duì)應(yīng)著查找名字時(shí)的local名字空間。

  2           6 LOAD_CONST               1 ('hello world') 

              9 STORE_NAME               1 (s)  #同理,獲取變量名s,出棧剛剛加載的字符串hello world,并存儲(chǔ)變量名和字符串hello world的對(duì)應(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對(duì)象,以code object和PyFrameObject的f_globals為參數(shù)創(chuàng)建函數(shù)對(duì)象PyFunctionObject并入棧
             18 STORE_NAME               2 (test)  #獲取變量test,并出棧剛?cè)霔5腜yFunctionObject對(duì)象,并存儲(chǔ)到local名字空間。

  9          21 LOAD_NAME                3 (__name__) ##LOAD_NAME會(huì)先依次搜索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對(duì)應(yīng)的指令處,也就是下面的POP_TOP。因?yàn)樵贑OMPARE_OP指令中,會(huì)設(shè)置棧頂為比較的結(jié)果,所以需要出棧這個(gè)比較結(jié)果。當(dāng)然我們這里是相等,所以接著往下執(zhí)行33處的指令,也是POP_TOP。
             33 POP_TOP             

 10          34 LOAD_NAME                2 (test) ##加載函數(shù)對(duì)象
             37 CALL_FUNCTION            0  ##調(diào)用函數(shù)
             40 POP_TOP                     ##出棧函數(shù)返回值
             41 JUMP_FORWARD             1 (to 45) ##前進(jìn)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不同,它是存儲(chǔ)到PyFrameObject的f_localsplus中,不是local名字空間。

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

  7          11 LOAD_GLOBAL              0 (s) #因?yàn)楹瘮?shù)沒(méi)有使用local名字空間,所以,這里不是LOAD_NAME,而是LOAD_GLOBAL,不要被名字迷惑,它實(shí)際上會(huì)依次搜索global,builtin名字空間。
             14 PRINT_ITEM          
             15 PRINT_NEWLINE       
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

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

關(guān)于字節(jié)碼指令,代碼中做了解析。需要注意到函數(shù)中局部變量如k的取值用的是LOAD_FAST,即直接從PyFrameObject的f_localsplus字段取,而不是LOAD_NAME那樣依次從local,global以及builtin查找,這是函數(shù)的特性決定的。函數(shù)的運(yùn)行時(shí)棧也是位于f_localsplus對(duì)應(yīng)的那片內(nèi)存中,只是前面一部分用于存儲(chǔ)函數(shù)參數(shù)和局部變量,而后面那部分才是運(yùn)行時(shí)棧使用,這樣邏輯上運(yùn)行時(shí)棧和函數(shù)參數(shù)以及局部變量是分離的,雖然物理上它們是連在一起的。

需要注意的是,python中使用了預(yù)測(cè)指令機(jī)制,比如COMPARE_OP經(jīng)常跟JUMP_IF_FALSE或JUMP_IF_TRUE成對(duì)出現(xiàn),所以如果COMPARE_OP的下一條指令正好是JUNP_IF_FALSE,則可以直接跳轉(zhuǎn)到對(duì)應(yīng)代碼處執(zhí)行,提高一定效率。

此外,還要知道在運(yùn)行test3.py的時(shí)候,模塊的test3.py棧幀對(duì)象中的f_locals和f_globals的值是一樣的,都是__main__模塊的字典。在test3.py的代碼后面加上如下代碼可以驗(yàn)證這個(gè)猜想。

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

正式因?yàn)槿绱?,所以python中函數(shù)定義順序是無(wú)關(guān)的,不需要跟C語(yǔ)言那樣在調(diào)用函數(shù)前先聲明函數(shù)。比如下面test4.py是完全正常的代碼,函數(shù)定義順序不影響函數(shù)調(diào)用,因?yàn)樵趫?zhí)行def語(yǔ)句的時(shí)候,會(huì)執(zhí)行MAKE_FUNCTION指令將函數(shù)對(duì)象加入到local名字空間,而local和global此時(shí)對(duì)應(yīng)的是同一個(gè)字典,所以也相當(dāng)于加入了global名字空間,從而在運(yùn)行函數(shù)g的時(shí)候是可以找到函數(shù)f的。

另外也可以注意到,函數(shù)聲明和實(shí)現(xiàn)其實(shí)是分離的,聲明的字節(jié)碼指令在模塊的PyCodeObject中執(zhí)行,而實(shí)現(xiàn)的字節(jié)碼指令則是在函數(shù)自己的PyCodeObject中。

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

def f():
  print 'function f'

g()
~

以上是Python執(zhí)行原理的分析的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI