python finally可以return吗_Python中的神坑return和finally

初识 return

返回一个值给调用者

deftest():

a= 2

returna

s=test()print(s)

#结果为2

如果return后面还有代码呢

deftest():

a= 2

returna

b= 3

returnb

s=test()print(s)

#结果依然为2,显然第一个return后面的代码没有执行

return 代表整个函数返回, 函数调用算结束

当 return + try..finally, 会怎样呢?

deftest():try:

a= 2

returnaexceptException as e:print(‘hahaha‘)finally:print(‘finally‘)

s=test()print(s)

结果:

finally

2

现在借助偷窥神器dis来一探究竟,挖掘最深处的秘密.

importdisdeftest():try:

a= 2

returnaexceptException as e:print(‘hahaha‘)finally:print(‘finally‘)print(dis.dis(test))

结果显示:

10 0 SETUP_FINALLY 56 (to 58)2 SETUP_EXCEPT 8 (to 12)11 4 LOAD_CONST 1 (2)6STORE_FAST 0 (a)12 8LOAD_FAST 0 (a)10RETURN_VALUE13 >> 12DUP_TOP14LOAD_GLOBAL 0 (Exception)16 COMPARE_OP 10(exception match)18 POP_JUMP_IF_FALSE 52

20POP_TOP22 STORE_FAST 1(e)24POP_TOP26 SETUP_FINALLY 14 (to 42)14 28 LOAD_GLOBAL 1 (print)30 LOAD_CONST 2 (‘hahaha‘)32 CALL_FUNCTION 1

34POP_TOP36POP_BLOCK38POP_EXCEPT40LOAD_CONST 0 (None)>> 42LOAD_CONST 0 (None)44 STORE_FAST 1(e)46 DELETE_FAST 1(e)48END_FINALLY50 JUMP_FORWARD 2 (to 54)>> 52END_FINALLY>> 54POP_BLOCK56LOAD_CONST 0 (None)16 >> 58 LOAD_GLOBAL 1 (print)60 LOAD_CONST 3 (‘finally‘)62 CALL_FUNCTION 1

64POP_TOP66END_FINALLY68LOAD_CONST 0 (None)70RETURN_VALUE

None

1. 第一列是代码在文件的行号

2. 第二列字节码的偏移量

3. 字节码的名字

4. 参数

5. 字节码处理参数最终的结果

在字节码中可以看到, 依次是SETUP_FINALLY 和 SETUP_EXCEPT, 这个对应的就是finally和try,虽然finally在try后面, 虽然我们通常帮他们看成一个整体, 但是他们在实际上却是分开的... 因为我们重点是finally, 所以就单单看SETUP_FINALLY

//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

voidPyFrame_BlockSetup(PyFrameObject*f, int type, int handler, intlevel)

{

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设置:

b_type (opcode 也就是SETUP_FINALLY)

b_level

b_handler (INSTR_OFFSET() + oparg)

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;gotofast_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);

}elsePUSH(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模块研究一下!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值