返回一個值給調用者segmentfault
def test(): a = 2 return a s = test() print(s)
#結果爲2
若是return後面還有代碼呢markdown
def test(): a = 2 return a b = 3 return b s = test() print(s)
#結果依然爲2,顯然第一個return後面的代碼沒有執行
return 表明整個函數返回, 函數調用算結束
當 return + try..finally, 會怎樣呢?
def test(): try: a = 2 return a except Exception as e: print('hahaha') finally: print('finally') s = test() print(s)
結果:函數
finally
2
如今藉助偷窺神器dis來一探究竟,挖掘最深處的祕密.oop
import dis def test(): try: a = 2 return a except Exception as e: print('hahaha') finally: print('finally') print(dis.dis(test))
結果顯示:this
10 0 SETUP_FINALLY 56 (to 58) 2 SETUP_EXCEPT 8 (to 12) 11 4 LOAD_CONST 1 (2) 6 STORE_FAST 0 (a) 12 8 LOAD_FAST 0 (a) 10 RETURN_VALUE 13 >> 12 DUP_TOP 14 LOAD_GLOBAL 0 (Exception) 16 COMPARE_OP 10 (exception match) 18 POP_JUMP_IF_FALSE 52 20 POP_TOP 22 STORE_FAST 1 (e) 24 POP_TOP 26 SETUP_FINALLY 14 (to 42) 14 28 LOAD_GLOBAL 1 (print) 30 LOAD_CONST 2 ('hahaha') 32 CALL_FUNCTION 1 34 POP_TOP 36 POP_BLOCK 38 POP_EXCEPT 40 LOAD_CONST 0 (None) >> 42 LOAD_CONST 0 (None) 44 STORE_FAST 1 (e) 46 DELETE_FAST 1 (e) 48 END_FINALLY 50 JUMP_FORWARD 2 (to 54) >> 52 END_FINALLY >> 54 POP_BLOCK 56 LOAD_CONST 0 (None) 16 >> 58 LOAD_GLOBAL 1 (print) 60 LOAD_CONST 3 ('finally') 62 CALL_FUNCTION 1 64 POP_TOP 66 END_FINALLY 68 LOAD_CONST 0 (None) 70 RETURN_VALUE None
1. 第一列是代碼在文件的行號 2. 第二列字節碼的偏移量 3. 字節碼的名字 4. 參數 5. 字節碼處理參數最終的結果
在字節碼中能夠看到, 依次是SETUP_FINALLY
和 SETUP_EXCEPT
, 這個對應的就是finally
和try
,雖然finally
在try
後面, 雖然咱們一般幫他們當作一個總體, 可是他們在實際上倒是分開的... 由於咱們重點是finally
, 因此就單單看SETUP_FINALLY
spa
// ceval.c TARGET(SETUP_FINALLY) _setup_finally: { /* NOTE: If you add any new block-setup opcodes that are not try/except/finally handlers, you may need to update the PyGen_NeedsFinalizing() function. */ PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, STACK_LEVEL()); DISPATCH(); } // fameobject.c void PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level) { PyTryBlock *b; if (f->f_iblock >= CO_MAXBLOCKS) Py_FatalError("XXX block stack overflow"); b = &f->f_blockstack[f->f_iblock++]; b->b_type = type; b->b_level = level; b->b_handler = handler; }
從上面的代碼, 很明顯就能看出來, SETUP_FINALLY
就是調用下PyFrame_BlockSetup
去建立一個Block
, 而後爲這個Block
設置:code
b_type (opcode 也就是SETUP_FINALLY
)orm
b_levelblog
b_handler (INSTR_OFFSET() + oparg
)get
handler 可能比較難理解, 其實看剛纔的 dis
輸出就能看到是哪一個, 就是 13 >> 31 LOAD_CONST 2 ('finally')
, 這個箭頭就是告訴咱們跳轉的位置的, 爲何會跳轉到這句呢? 由於6 0 SETUP_FINALLY 28 (to 31)
已經告訴咱們將要跳轉到31這個位置~~~
若是這個搞清楚了, 那就再來繼續看 return
, return
對應的字節碼是: RETURN_VALUE
, 因此它對應的源碼是:
// ceval.c TARGET_NOARG(RETURN_VALUE) { retval = POP(); why = WHY_RETURN; goto fast_block_end; }
原來咱們之前理解的return
是假return
! 這個return
並無直接返回嘛, 而是將堆棧的值彈出來, 賦值個retval
, 而後將why
設置成WHY_RETURN
, 接着就跑路了! 跑到一個叫fast_block_end;
的地方~, 沒辦法, 爲了揭穿真面目, 只好掘地三尺了:
while (why != WHY_NOT && f->f_iblock > 0) { fast_block_end: while (why != WHY_NOT && f->f_iblock > 0) { /* Peek at the current block. */ PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1]; assert(why != WHY_YIELD); if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) { why = WHY_NOT; JUMPTO(PyInt_AS_LONG(retval)); Py_DECREF(retval); break; } /* Now we have to pop the block. */ f->f_iblock--; while (STACK_LEVEL() > b->b_level) { v = POP(); Py_XDECREF(v); } if (b->b_type == SETUP_LOOP && why == WHY_BREAK) { why = WHY_NOT; JUMPTO(b->b_handler); break; } if (b->b_type == SETUP_FINALLY || (b->b_type == SETUP_EXCEPT && why == WHY_EXCEPTION) || b->b_type == SETUP_WITH) { if (why == WHY_EXCEPTION) { PyObject *exc, *val, *tb; PyErr_Fetch(&exc, &val, &tb); if (val == NULL) { val = Py_None; Py_INCREF(val); } /* Make the raw exception data available to the handler, so a program can emulate the Python main loop. Don't do this for 'finally'. */ if (b->b_type == SETUP_EXCEPT || b->b_type == SETUP_WITH) { PyErr_NormalizeException( &exc, &val, &tb); set_exc_info(tstate, exc, val, tb); } if (tb == NULL) { Py_INCREF(Py_None); PUSH(Py_None); } else PUSH(tb); PUSH(val); PUSH(exc); } else { if (why & (WHY_RETURN | WHY_CONTINUE)) PUSH(retval); v = PyInt_FromLong((long)why); PUSH(v); } why = WHY_NOT; JUMPTO(b->b_handler); break; } } /* unwind stack */
在這須要回顧下剛纔的一些知識, 剛纔咱們看了return
的代碼, 看到它將why
設置成了 WHY_RETURN
, 因此在這麼一大串判斷中, 它只是走了最後面的else
, 動做也很簡單, 就是將剛纔return
儲存的值retval
再push
壓回棧, 同時將why
轉換成long
再壓回棧, 而後有設置了下why
,接着就是屁顛屁顛去執行剛纔SETUP_FINALLY
設置的b_handler
代碼了~ 當這這段bhandler
代碼執行完, 就再經過END_FINALLY
去作回該作的事, 而這裏就是, return retval
總結:
因此, 咱們應該能知道爲何當咱們執行了return
代碼, 爲何finally
的代碼還會先執行了吧, 由於return
的本質, 就是設置why
和retval
, 而後goto
到一個大判斷, 最後根據why
的值去執行對應的操做! 因此能夠說並非真的實質性的返回. 但願咱們日後再用到它們的時候, 別再掉坑裏!
dis模塊研究一下!