何时建议使用ret_val变量?
我看到关于以下代码哪种写法更好的意见不一:
def function():
ret_val = 0
if some_condition():
ret_val = 2
else:
ret_val = 3
return ret_val
还是这种写法更好:
def function():
if some_condition():
return 2
else:
return 3
这是一个简单的例子,我用的是类似Python的风格,但我想了解的是一个通用的原则,什么时候应该使用一个“累加器”变量来跟踪返回值,或者什么时候可以使用多个退出点。我知道不同的编程语言可能会有不同的理由来选择一种风格,所以我希望能听到不同的观点,了解为什么某种语言可能会坚持某种特定的写法。(具体来说,我以前听说C语言的结构化编程避免在函数中有多个退出点。)
11 个回答
在Python中,函数中间出现return语句是很常见的,特别是当你想提前结束函数时。你的例子通常会被改写成这样:
def function():
if some_condition():
return 2
return 3
也就是说,当if语句后面有return时,你可以省略else的部分。
除非真的没办法,否则不要使用累加器。因为它会让你的程序变得复杂,增加了状态和分支,你需要手动去跟踪这些。通过提前返回,你可以减少代码中的状态和分支数量。
我以前听说在C语言中,结构化编程避免一个函数有多个退出点。
其实正好相反——结构化编程不鼓励多个入口点,但多个退出点是可以接受的,甚至是推荐的(比如“保护性语句”)。
我们是不是忘了为什么“多个退出点”最开始被认为是有害的呢?在过去(在大家普遍没有好的异常处理和 finally 结构,或者像 auto_ptr
这样的管理对象来清理资源的时候),很多有多个退出点的函数就成了一个麻烦:
int function blah(arg1, arg2)
allocate resource
if early failure detection
return failure_status
... much later...
release resource // oh rats! resource didn't release
return success_status
如果这个资源是内存,那就会造成内存泄漏。如果是数据库事务,那就可能导致数据库争用或死锁。再说了,随着异常处理支持的增加,我们实际上给方法添加了很多潜在的退出点(因为可能会有未处理的异常)。在我写 C++ 的时候,我养成了一个习惯:从不直接调用 delete,而是使用 auto_ptr
,这样当 auto_ptr
离开作用域时,分配的内存就会被自动清理,即使出现了意外的异常。
在我们有垃圾回收的 Python 世界里,这个问题依然存在,尽管我们很多对象,比如文件或锁,已经有了更好的自我清理机制。但在 CPython 以外的实现(比如 jython 和 IronPython)中,无法保证析构函数何时会被调用,所以需要在你的方法中加入一些更主动的清理措施。最早为此设计的机制就是 try/finally:
int function blah(arg1, arg2)
allocate resource
try:
if early failure detection
return failure_status
... much later...
return success_status
finally:
release resource // always releases no matter what
不过现在 Python 有了上下文管理器,配合新的 'with' 语法:
int function blah(arg1, arg2)
allocate resource
with context_manager(resource): // releases on exit from 'with'
if early failure detection
return failure_status
... much later...
return success_status
所以我们要确保讲清楚整个故事,之所以可以抛弃这个老问题,是因为新的编码实践让它变得不再必要。