深度詳解 Python yield與實現(xiàn)

2018-02-15 14:17

 

Python yield與實現(xiànσφπ)

yield的(de)功能(néng)類似于return,但(dàn)是(shì)不(bù)同之處在于它返回的(de)Ω✘是(shì)生(shēng)成器(qì)

 

生(shēng)成器(qì)

 

生(shēng)成器(qì)是(shì)通(tōng)過一(yī)個(gè)↔≈或多(duō)個(gè)yield表達式構成的(de)函數(shù),每一(yī)個(gè)₩λ生(shēng)成器(qì)都(dōu)是(shì)一(yī)個(g₩ αγè)叠代器(qì)(但(dàn)是(shì)叠代器( ☆™εqì)不(bù)一(yī)定是(shì)生(shēng)成☆σ✘$器(qì))。

如(rú)果一(yī)個(gè)函數(shù)包含yield關鍵字,這(zhè)個(gè)函數(shù)就(ji←₽ ¥ù)會(huì)變為(wèi)一(yī)Ω€₽&個(gè)生(shēng)成器(qì)。

生(shēng)成器(qì)并不(bù)會(huì)一(yī)次返回β↔ ¥所有(yǒu)結果,而是(shì)每次遇到(dào)yield關鍵字後返回相(xiàng)應結果,并保留函數(shù)當前的(de)運行 •λ(xíng)狀态,等待下(xià)一(yī)次的(de)調用(yòn'®¶g)。

由于生(shēng)成器(qì)也(yě)是(shì)一(yī)₩★π個(gè)叠代器(qì),那(nà)麽它就(jiù)應該支持next方法來(lái)獲取下(xià)一(yī)個(gè)值φ¥。

 

基本操作(zuò)

 

# 通(tōng)過`yield`來(lái)® 創建生(shēng)成器(qì)

def func():

   for i in±‍₽ xrange(10);

    &nbs±σγp;   yie₹♥¥♥ld i

 

# 通(tōng)過列表來(lái)創建生(shēng)成器(qì)↑↕∞☆

[i for i in&nb☆∞≤sp;xrange(10)]

 

# 調用(yòng)如(rú)下(xià)λ♥

>>> f = func()

>>> f # 此時(sh∞"λí)生(shēng)成器(qì)還(hái)沒有(yǒu)運行(xíng)←±

<generator object func&nbs✔Ω'p;at 0x7fe01a85382₩€ 0>

>>> f.next() # 當i=☆σ≤0時(shí),遇到(dào)yield關Ω✔®Ω鍵字,直接返回

0

>>> f.next()&≤€↓nbsp;# 繼續上(shàng)一(yī)次↕♣≈λ執行(xíng)的(de)位置,進入下(α±xià)一(yī)層循環

1

...

>>> f.next()

9

>>> f.‍≠next() # 當執行(xíng)完最後一(yī)次循環β♣§後,結束yield語句,生(shēng)成StopIterationφε異常

Traceback (most recent c↔<δ§all last):

  File &quΩ™ε÷ot;<stdin>",&nbs'€÷p;line 1, &$in <module>←©;

StopIteration

>>>

 

除了(le)next函數(shù),生(shē‍¶ αng)成器(qì)還(hái)支持send函★↔™數(shù)。該函數(shù)可(kě)以向生(shēng)成器(q₽¥ì)傳遞參數(shù)。

 

>>> def func"₩Ω():

...     n&¶↓↑₽nbsp;= 0

...     γβ↕while 1:

...    &n§δbsp;    n =&nb∑¥✘‍sp;yield n #可α↑<(kě)以通(tōng)過send函數(sh≠σù)向n賦值

...

>>> f =&nbs'"p;func()

>>> f.n♦≠ext() # 默認情況下(xià)n為(wèi)0

0

>>> f.send(1) #÷εn賦值1

1

>>> f.s‌÷Ωend(2)

2

>>>

 

應用(yòng)

 

最經典的(de)例子(zǐ),生(shēng)成無限序列。

常規的(de)解決方法是(shì),生(shēng)★¥成一(yī)個(gè)滿足要(yào)求的♦'α≤(de)很(hěn)大(dà)的(de)列表,這(zhè)個(gè)£₽列表需要(yào)保存在內(nèi)存中,很(hě☆∞• n)明(míng)顯內(nèi)存限制(zhì)了(le)這(zhè)個(g₩ <è)問(wèn)題。

def get_primes(start):

    for e"∞lement in magica₽¶βl_infinite_range(start):

    &nb≠εsp;   i∑Ωf is_prime(element):

   &nb‍ "sp;    ₹↔β    return e$₩$lement

 

如(rú)果使用(yòng)生(shēng)成器(qì)就(ji±α♣​ù)不(bù)需要(yào)返回整個(gè)列表,每次都(dō∞×u)隻是(shì)返回一(yī)個(gè)數(shù)據,避免了(λ∑σle)內(nèi)存的(de)限制(zhì)問(wèn∑ )題。

 

def get_primes(numbeα r):

    whilΩ"↕¶e True:

   &nbs←±​p;   &nα♥bsp;if is_prime(number):

      &n★✔¶>bsp;   &nbφ©sp; yield number

   &nbsπβ≠p;   &nbsγ₩≈εp;number += 1

 

 

生(shēng)成器(qì)源碼分(fēn)析♣λ

 

生(shēng)成器(qì)的(de)源碼✘≈ λ在Objects/genobject.c

 

調用(yòng)棧

 

在解釋生(shēng)成器(qì)之前,需要(yào)講解一(yī)下(↔<xià)Python虛拟機(jī)的(de)調用(yòng)原理(lǐαε☆€)。

Python虛拟機(jī)有(yǒu)一(yī)個(gè)棧幀¶₩的(de)調用(yòng)棧,其中棧幀的(de)是(shì)PyFrameObject,位于Include/frameobject.h

typedef struct _fra<© me {

    PyObjec¶¥∑☆t_VAR_HEAD

    struct&nb±€←±sp;_frame *f_back; &nbs&≤p;/* previous frame, or NULL */

    PyCodeObjec≠"t *f_code;   ↑‌♦;/* code segment */

    Py"∑πObject *f_builtins;  &δ←nbsp;/* builtin symbol table (P>ε$ yDictObject) */

    PyObjec≥₩>t *f_globals;  &nbs‌$☆↓p; /* global symbol tableεα​ (PyDictObject) */

    PyObje÷₹δct *f_locals;     ≤₩☆; /* local symbol table (any mappi≥¶σ±ng) */

    PyObject **f_va← Ω∞luestack;    /✘♣* points after the last local */

    ✔≈© ;/* Next free slot in f_valuestack.&nb∑φφβsp; Frame creation "♣¥sets to f_valuestack.

    &nb↕™♣sp;  Frame evaluation usually¥$≤ NULLs it, but a frame that yields set≤★✔s it

    &n"÷↓bsp;  to the current>✔ stack top. */

    ₩α;PyObject **f_stacktop;

    PyObjec↔≈×t *f_trace; &nb✘¶sp;    /* Trace fun♠₽Ω☆ction */

 

    /* If an exc ≈★✔eption is raised in this frame, the ne‍↓xt three are used to

     * record the e×♣←xception info (if any) originally  Ω₽in the thread state. &n​↔★bsp;See

     * comments bπ•♠efore set_exc_info() -- it's ↑ not obvious.

     *≥β₩• Invariant:  if _✘→∞πtype is NULL, then so are ≈α÷≈_value and _traceback.

     *σ&₽> Desired invariant:  all÷$¥ three are NULL, or all t$'hree are non-NULL. &nbsα™< p;That

     * on‌☆©&e isn't currently  •δεtrue, but "should be≠↔™".

     */

    PyObject ≈∞$$*f_exc_type, *f_exc_valu‍φe, *f_exc_trace≠ €back;

 

    PyThreadSt♣γ₹ate *f_tstate;

   &nb≥÷sp;int f_lasti;♠♦∏     ∞©♣;   /* La←←δσst instruction if called */

    ‍∑♣®/* Call PyFrame_GetLineNumbe↑β☆$r() instead of reading₹εΩ this field

      φ±; directly.  As of 2.3 f_li↓ neno is only valid when tracinδ✘£♥g is

       a€​✘"ctive (i.e. when f_trace is>♠γ set).  At other ti♣≥mes we use

    ™♥≈&;   PyCode_Addr2Line to ca•★β←lculate the line from the currenγ¥t

      ∏"; bytecode index. */

    int f_line₽σπno;    &n↓±bsp;  /* Curren♥∞§ t line number */

   &nb<₹©♥sp;int f_iblock; ✘σ ₹;     ε♣ε; /* index in f_blockstack *α∞↔ /

    PyTryBlock&≠×→nbsp;f_blockstack[CO_MAXBLOCKS];←®☆ /* for try and loop blocks ∞↕*/

    PyObject *f_loc↑☆alsplus[1];  /* locals+stack,ε™ dynamically sized */

} PyFrameObject;

 

棧幀保存了(le)給出代碼的(de)的(de)¥∞×₹信息和(hé)上(shàng)下(xià)文(wén),其中包含最後∏∑≈✘執行(xíng)的(de)指令,全局和(hé)•←局部命名空(kōng)間(jiān),異常狀态等信λ≥₩息。f_valueblock保存了(le)數(shù)據,b_blockstack保存了(le)異常和(hé)循環控制(zhì)方法。

舉一(yī)個(gè)例子(zǐ)來(lái)說(shuō)明(mí"<ng),

def foo():

    x = ×ε​1

    def b≈✔ar(y):

      & ≠↔nbsp; z = y +&nb≈∞•♥sp;2  #

 

那(nà)麽,相(xiàng)應的(de)調用(yòng)棧如(♠∞↑rú)下(xià),一(yī)個(gè)py文(wén)件(∞•♣jiàn),一(yī)個(gè)類,一(yī)個(gè)函數(shù)都(dπ₩€ōu)是(shì)一(yī)個(gè)代碼塊,對(duì)應者一(y≠>ī)個(gè)Frame,保存著(zhe)上(shàng)下₩€₩±(xià)文(wén)環境以及字節碼指令。₹≥φ

 

c   -------------------------¥¶γ→--

a  | bar Fram ∞↕≥e    ✔₽¥λ    &nbs☆©® p;   &nb™∑'sp;    | -€∏> block stack: []

l  |    ←©;  (newest)&n"‍±βbsp;     &n≈ bsp;  &nbsδ₩p;    | ->✔  data stack: [1, 2'π©]

l   -------------------------¥♠‌--

   | foo δ↕;Frame   &Ωπ↔≈nbsp;   &nbs→£'p;   &nbs♠☆p;     | ->&nbs ‌"&p;block stack: []¶‌★₹

s  |    ¶πφ‍;   &nλ bsp;   ×§↔;   &nbsσε​₩p;   →•✘     &nb✘¶↕≤sp;   | -> data&n≥←bsp;stack: [.bar at ♠  0x10d389680>, 1]

t   --------↓↔Ω★-------------------

a  | mai¥≤↑♥n (module) Frame &nbs∞↑♠p;     | -& →≈gt; block stack:&nbs$→'γp;[]

c  |   "∏✘↓;    (oldest) >λ¥     &nbs¥♦p;   &n↕§bsp; | -> data stac‌£φ∏k: []

k   ----------✘≥-----------------

 

每一(yī)個(gè)棧幀都(dōu)擁有(yǒu)自(δ↕∑₹zì)己的(de)數(shù)據棧和(hé)block棧,獨立的(de)數(s←§★λhù)據棧和(hé)block棧使得(d×§≥e)解釋器(qì)可(kě)以中斷和(hé)恢複棧幀(生(shēn♠☆g)成器(qì)正式利用(yòng)這(zhè)點)。

Python代碼首先被編譯為(wèi)字節碼,再由÷ Python虛拟機(jī)來(lái)執₽φ←行(xíng)。一(yī)般來(lái)說(shuō),一(©‍₹yī)條Python語句對(duì)應著(zhe)多(duō)條∞δ字節碼(由于每條字節碼對(duì)應著(zhe)一(yī)條C語句,‌λ₩而不(bù)是(shì)一(yī)個(gè)機(jī)器(qì""©∏)指令,所以不(bù)能(néng)按照(zhà€¥←o)字節碼的(de)數(shù)量來(lái)判斷代碼性能( €₹néng))。

調用(yòng)dis模塊可(kě)以分(fēn)析字節碼,

from dis import dis

 

dis(foo)

 

  5 &n ®₽bsp;   &n≥÷★bsp;     0&nb♠€$sp;LOAD_CONST  ¶ δ     &nb¶' ₹sp;   &n≥ &‌bsp;   1 (1) ♦∑¥®;# 加載常量1

   &nbs"'≥$p;       '≤    ©•;3 STORE_FAST  &₹¶nbsp;     &n"→bsp;  &nbs✘→p;   0 (x)€δ # x賦值為(wèi)1

 

  6  ♦→;     &nbsδ‌♠p;   6 LOAD_CONST&<©λ nbsp;     λ•;   &nbs✘"p;   &nφ↑bsp; 2 (<code>)&nb$★€sp;# 加載常量2

      &&•♠×nbsp;     ∏®™  9 MAKE_FUNCTION&n™©₩πbsp;     &nb∑γ<sp;    Ω∑; 0 # 創建函數(shù)

      &nb★→sp;   ± σ;   12&n€€±bsp;STORE_FAST   &nbε♠₩sp;   ∞‌←÷    &nbΩΩ↑&sp;   1&nbs≤λ¥p;(bar)

 

  9 &n≥☆bsp;   &nb​≤sp;   &nb♣₹sp;15 LOAD_FAST  ÷≠;    δ‌§;     &n∑¥  bsp;   &nσ↑±♣bsp;1 (bar)

      ≥λ    &€♣nbsp;  18&nb★≠"sp;LOAD_FAST    &n✘‌¥∏bsp;    ₽₽♥;     &"  0 (x)

    &nb≤₽‍sp;   &nb♠±sp;   &n♣ ≥bsp;21 CALL_FUNCTI✔™​€ON   &‌Ω∑nbsp;     &nbε∑ sp;  1 &‍₩nbsp;# 調用(yòng)函數(shù)

   &nbs"ε↕p;     Ω•☆±     ‌α♥;24 RETURN_VALUE  &≠Ω ↔nbsp;     &σ≈£lt;/code>

其中,

第一(yī)行(xíng)為(wèi)代碼行(xíng)号;

第二行(xíng)為(wèi)偏移地(dì)址;

第三行(xíng)為(wèi)字節碼指令;

第四行(xíng)為(wèi)指令參數(shù);

第五行(xíng)為(wèi)參數(shùπ¥)解釋。

 

 

生(shēng)成器(qì)源碼分(fēn)析

 

由了(le)上(shàng)面對(duì)于調用(yòng)棧的(de)理(λ≥lǐ)解,就(jiù)可(kě)以很(hěn)容易的(de)≈ 明(míng)白(bái)生(shēng)成器(qì★✘★)的(de)具體(tǐ)實現(xiàn)。

生(shēng)成器(qì)的(de)源碼位于object/genobject.c

生(shēng)成器(qì)的(de)創建

 

PyObject *

PyGen_New(PyFrameObject㥩 *f)

{

    PyGenObject *¶&£gen = PyObjecγαt_GC_New(PyGenObject, &♥©;PyGen_Type); # 創建生(shēng)成器( §★★qì)對(duì)象

    if ☆γ→;(gen == NULL) ♠∑{

    &nbs¥ ←p;   Py_DECREF(f≥≥);

      ≤Ω∞™  return NULL;¥©←×

    ÷‌;}

    gen-φ§§>gi_frame =&nbs™✔p;f; # 賦予代碼塊

    Py_INCREF(f- '>f_code); # 引用(yòng)計(jì ¥∑)數(shù)+1

   &nb≠>sp;gen->gi_code&nbs≠π♦p;= (PyObject *)(f-λ‌>f_code);

    gen->gi_α∞running = 0; # 0表示 γ↕≈為(wèi)執行(xíng),也(yě)就(jiù)是(shì)生®&♦'(shēng)成器(qì)的(de)初始狀态

    gen->g → ★i_weakreflist = NULL;

    _PyObject_GC_TR↕≠§ACK(gen); # GC跟蹤

    reφ¥turn (PyObject *)gen;↔↓α

}

 

send與next

nextsend函數(shù),如(rú)下(xià)

 

static PyObject *

gen_iternext(PyGenObject *gen)

{

    r✘©‍eturn gen_send_ex(gen,&nb★∑sp;NULL, 0);

}

 

 

static PyObject ←☆*

gen_send(PyGenObject *gen, P₽€♥yObject *arg)

{

    r★₽←"eturn gen_send_ex(g £↕en, arg, 0);

}


從(cóng)上(shàng)面的(de)​ε©←代碼中可(kě)以看(kàn)到(dào),send和(hé)next都(dōu)是(shì)調用(yòng)的(de)同一(yī)函數(sh≠γε$ù)gen_send_ex,區(qū)别在于是(shì)否帶有(yǒu)參數(shù)。

 

 

static PyObject *

gen_send_ex(PyGenObject *gen, P≠♣yObject *arg, i©£↕≠nt exc)

{

    PyThreadS ↓ ₹tate *tstate = PyTh∑​readState_GET();

    PyFrameO€×bject *f = gen-&±>₹&gt;gi_frame;

    PyObject *resul‍™∞δt;

 

    if (ge$‌<n->gi_running) { # 判斷生(sφ↕hēng)成器(qì)是(shì)否已經運行(xí≈₽ng)

    φβ×δ    PyErr_Se×'tString(PyExc_ValueErro★‌£r,

        ≠γ    &n‌∑​★bsp;     ©≈↔×     &nbs‌σ‌p;  "gene≤★ §rator already executing"ε÷∑×;);

    &"≠nbsp;   return&±✔nbsp;NULL;

    }

    if&nbs♥↔☆×p;(f==NULL || f->fσ♦±♥_stacktop == NULL)σ©↕✔ { # 如(rú)果代碼塊為(wèi)空±•‌(kōng)或調用(yòng)棧為(wèi)空(kōng),則×₹抛出StopIteration異常

      &n₩±bsp; /* Only set exception if&  called from send() */

     ≥ ≥;   if (arg&nb®‌εsp;&& !exc)

     &nb€∑‍sp;   €₹;   PyErr_Set≠"$None(PyExc_StopIteration);

     & ₽   return NULL;

    }

 

    "​✘if (f->f_lasti ×♠↕‌== -1) { # f_lasti=1 ♣φ¶代表首次執行(xíng)

      φφ><;  if (arg&nbs∞ Ω®p;&& arg®>∑ != Py_None) δ { # 首次執行(xíng)不(≥♥≈πbù)允許帶有(yǒu)參數(shù)

     &nbs←δφp;    &nb‍ε♦sp; PyErr_SetStrin←£¥g(PyExc_TypeError,

      &nb✔$ sp;    &nbsφ↑♠♥p;    &‌✔∞ nbsp;    &nbs©←p;   &∑αnbsp;  "can't sen∑♠d non-None value to a "

     &n>↑∞bsp;    &nbs≠∑¶p;    &nb™★≤‍sp;    &n™→™bsp;     &nbs ↓p; "just-started generator&$→quot;);

    &✘λ$nbsp;    &n•δbsp;  return&nγ™<←bsp;NULL;

      &nb>±¥sp; }

    }&nbββsp;else {

      ↓∞$✔;  /* Push arg®✔€∏ onto the frame's value stack */

      &nbβ★sp; result = arg&nε¶↓bsp;? arg : Py_None;

      &n​ &≠bsp; Py_INCREF(result); # 該參數'←₹(shù)引用(yòng)計(jì)數(shù)+1

    ↔→    *(f->f_sta§≠♣↓cktop++) = result; #÷> 參數(shù)壓棧

    }

 

    /*>§§β Generators always return to their m↕ ost recent caller, not

    ​♦; * necessarily their creator.®→  */

    f->&¶Ω‍f_tstate = tstate;

   &nbs♣₽₽♠p;Py_XINCREF(tstate->frame);

    assert(f-₹"₹>f_back == NULL);

    f->fαδ_back = tstate-&₩ε∏gt;frame;

 

    gen-&∞σgt;gi_running =&♦‌nbsp;1; # 修改生(shēng)成器(q✘♣ ♥ì)執行(xíng)狀态

    res€ "ult = PyEva₩≈l_EvalFrameEx(f, exc)​σ; # 執行(xíng)字節碼

    gen->gi_ru∑↕nning = 0;™₹★∏ # 恢複為(wèi)未執行(xíng)狀δ☆♥态

 

    /* Do→‍✔×n't keep the reference ✘ ★>to f_back any longer than neceε¶©∑ssary.  It

     * < may keep a chain of frames aliv→δ✔↓e or it could create a re±σference

     * cycle.  ☆&*/

    ™β ¥;assert(f->f_back ==∞ ↔ tstate->frame);

    P♣ δy_CLEAR(f->f_back​↕);

    /*☆≠  Clear the borrowed referenc‍♣e to the thread state *♠§∑/

    f↓ ©->f_tstate = →♣§NULL;

 

    /* If ↑δ" the generator just returned (as ≥πγopposed to yielding), ×‍​≈signal

     *₩™ that the generator is exhaus↓"∏ted. */

    if (resu♣€δ‍lt == Py_None &ε≥σ& f->f_stacktop ==&n•∑≠bsp;NULL) {

     ↓"   Py_DE¶πCREF(result);

     &nbs‌∑♥p;  result = ≠≈∑NULL;

    &nbsααγp;   /* Set ex♣÷β ception if not called by gen_it♦™φ♠ernext() */

     &nbsλ≥≈∞p;  if (arg)

    ∞™    &nbs↔&↓ p;   PyE♣$↔rr_SetNone(PyExc_StopIt€≥eration);

    }

 

    if Ω≥£λ;(!result || f->f_sta✔Ωcktop == NULL≈‌) {

      β∞  /* generator canε♣9;t be rerun, so release tπ>↕∏he frame */

      &nb↑★sp; Py_DECREF(f);

     &nb✔↑sp;  gen->gi_frame ←¥= NULL;

    }

 

    return γ£​result;

}

 

字節碼的(de)執行(xíng)

 

PyEval_EvalFrameEx函數(shù)的(de)功能(néng)為(wèi)執行(x∑δ&íng)字節碼并返回結果。

# 主要(yào)流程如(rú)下(xià),

for (;;) {

   swit←↔βch(opcode) { # opcode©↔為(wèi)操作(zuò)碼,對(duì)應著(z₽↑♥$he)各種操作(zuò)

     &nb ♠¶>sp;  case NOPσλβ$:

   &nbs§↕p;    &n& bsp;   goto &nλ'bsp;fast_next_opcode;

    &n$₩​∑bsp;   ...©" 

     &nbs© βΩp;  ...

      &<∞♦∑nbsp; case YIELD_VALUE:&ε‌nbsp;# 如(rú)果操作(zuò)碼是(shì)yield

      &nb±Ω±γsp;    &nb₽≥¶sp;retval = < ★;POP();

     &nbsλ≈p;      ♠ ;f->f_stacktop = sta☆γ↔"ck_pointer;

      &nb§§λ✔sp;    β™±; why = WHY_σ∞≤λYIELD;

      ↔≤‍;   &n≈↔γ♥bsp;  goto fast_δ≠£©yield; # 利用(yòng)goto跳(tiào)'®©₩出循環

   &nb±β sp;}

}

 

fast_yield:

   &nb∞®×≥sp;...

return vetval; # ∑ 返回結果

 

舉一(yī)個(gè)例子(zǐ),f_back上★€γ✔(shàng)一(yī)個(gè)Frame,f_lasti上(∞↕©☆shàng)一(yī)次執行(xíng)的(de)指令的↓©​‌(de)偏移量,

 

import sys

from dis import dis

 

 

def func():

    f =&φδnbsp;sys._getframe(0)

    print f.¥♠'δf_lasti

    print f.f​→₽_back

    •&→yield 1

 

   &n♥↔α≈bsp;print f.f_lasti

    print f.f_'γback

    yield 2

 

 

a = func()

dis(func)

a.next()

a.next()


結果如(rú)下(xià),其中第三行(xíng)的(de)英文(wén)為£↔(wèi)操作(zuò)碼,對(duì)應著(zhe<​)上(shàng)面的(de)opcode,每次switch都("↓≤dōu)是(shì)在不(bù)同的(de)opcodeπ♦✔之間(jiān)進行(xíng)選擇。

 

6     σσ∞≥      λ± 0 LOAD_GLOBAL  &nλ≠♠bsp;     ↑∑;     &n•≈bsp;0 (sys)

    &nbs£±p;      ₹✘​​   3 LOAD_ATTR ≥™π;    ÷ ‍     γ♣;     ε  1 (_getfram€δ€e)

   &nbs÷<p;      πα ;    6&nbs≠™®p;LOAD_CONST    ®ε      &nb∞‌sp;    1&φε<nbsp;(0)

      ☆ ™β;   &σ∞←nbsp;    9&δ★ nbsp;CALL_FUNCTION &nbs&↔p;    &n×∞ε®bsp;   &nbφ"•©sp; 1

     &n≥→‌¥bsp;    ¥≠   12 STORE₹γ_FAST     &‌♥βnbsp;     γ♥​    0&n<× bsp;(f)

 

  7  &n♦φσbsp;    §ε←;   15 LOAD€✔_FAST   &nbs₹××p;    &nbs÷¥p;   &nbs♣​ φp;   0 ★↕β ;(f)

    &nb♥♦ασsp;      λ↔;  18 LOAD_ATTR&n♦"↓bsp;  &nbs®↕★¶p;     &nb✔®€‌sp;    &n↔₹→bsp; 2 (f_lasti)

     &nbs≠≠p;    &nb​≤•±sp;  21 P✘​RINT_ITEM    &n♣§∏bsp;  &nbs≤→p;  

   &nbs£≥≠≤p;   &n&αbsp;   &nb•≥sp; 22 PRINT_NE₽₹WLINE     &nbs↕∞ p;

 

  8    ≤λ∏£;   &nbs≥£∏p;  23 LOAD_FA>↔$♠ST    ♥β     Ω€→;      &​​∞nbsp;0 (f)

    &↔σnbsp;    &n±€©bsp;   26 LOA¶βλD_ATTR    &nb$>δsp;   &nb®λsp;    ↔×;   3 (f_back)

     ™​σ;      &β₹nbsp; 29 PRINT_ITEM &±✔Ω✔nbsp;  &nbs‍§≥☆p;      ≤;

     &n €bsp;    &nb"÷✔×sp;  30 PRINT_N€ε→EWLINE     &nb÷€€£sp;

 

  9   &nb♥≤☆sp;   ₽✘δ≤;   31 ≈>​λ;LOAD_CONST  &nbs♦₽≈p;    &nb£☆♠∏sp;     &n•♠±♣bsp; 2 (1)

      ₽§₽      &nb↓→sp;34 YIELD_VALUE  &n→≠☆bsp;  # 此時(shí♣♠ )操作(zuò)碼為(wèi)YIELD_VALUE,直接跳(tiào)>>轉上(shàng)述goto語句,此時(shí©$)f_lasti為(wèi)當前指令,f_back為(wèi)當∑∑✘>前frame

    £ &&    &nbφ®★$sp;    35∞£  POP_TOP    &n§ bsp;     &​§$nbsp; 

 

11     & ∑nbsp;    36α≥¶ LOAD_FAST   &•±£ nbsp;     &nb↕↑sp;     →¥; 0 (f)

     &nbδ±®®sp;   ¥♥ λ    39 LOAD_γ♦♣ATTR  &nbs≠>p;   &✔∏→&nbsp;    &$>α;     2&↔α‍₽nbsp;(f_lasti)

     &nσ÷bsp;   &"✘♣nbsp;   42 P'₽Ω≥RINT_ITEM   &nbs✔φp;    &nb↓♦sp; 

     &nb© sp;    &‍↕nbsp;  43 PRINT_NΩλEWLINE   α•   

 

12    &nb€ sp;     ™$≈φ;44 LOAD_FAST &nbs♣♦​≠p;     &nbs$Ωp;    &nb¥&÷‍sp;   ¥♠•↓;0 (f)

      &nb♠&sp;    & '®nbsp; 47 LOAD_ATTR&nb ↔ sp;     &nbsδ±↕p;      ↓←α$;   3 (f_back)

    &n₹♥∑↓bsp;     &♥φ$≠nbsp;  50 PR'λ¥INT_ITEM   ελ©§    ≈≠λ   

    &n α↑bsp;   &n↕§&bsp;    51 ↔ <;PRINT_NEWLINE &nbs> p;    

 

13    π≥      52&↕•nbsp;LOAD_CONST  •♦      ‍'ε;     &nbs↔®p; 3 (2)

    &$™nbsp;     &n&€bsp;  55 YIELD&€_VALUE  &n±¶bsp;     β≤λ♠;

     ∑γ§≈;    ™λ®&    56 POΩ£P_TOP    &n<→bsp;     &nbs±↑>↓p; 

   &nb♠←sp;    &÷Ω nbsp;   &nb✔φ¶sp;57 LOAD_CONST&nbs≥★ αp;      &←¶¥δnbsp;   &nb÷÷♠αsp;   0 ( ↔  None)

     &nb£↓sp;   ‌     60&n®✘βbsp;RETURN_VALUE   &↓‍↕εnbsp;   &n&ε₹bsp;

18

<frame object at 0x7♠♥→fa75fcebc20> #和(hé)下(xià) ←÷面的(de)frame相(xiàng)同,屬于同一(yī)個(∞&gè)frame,也(yě)就(jiù)是✘'£(shì)說(shuō)在同一(yī)個(gè)函數(shù)★∞(命名空(kōng)間(jiān))內(nèi)♦±♠>,frame是(shì)同一(yī)個(gè)。

39

<frame object at 0x7fa75fc↕÷★ ebc20>

 

 

來(lái)源: cococo點點

http://www.cnblogs.com/coder20≠"12/p/4990834.html