何时建议使用ret_val变量?

3 投票
11 回答
1474 浏览
提问于 2025-04-15 14:39

我看到关于以下代码哪种写法更好的意见不一:

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 个回答

5

在Python中,函数中间出现return语句是很常见的,特别是当你想提前结束函数时。你的例子通常会被改写成这样:

def function():
    if some_condition():
        return 2
    return 3

也就是说,当if语句后面有return时,你可以省略else的部分。

5

除非真的没办法,否则不要使用累加器。因为它会让你的程序变得复杂,增加了状态和分支,你需要手动去跟踪这些。通过提前返回,你可以减少代码中的状态和分支数量。

我以前听说在C语言中,结构化编程避免一个函数有多个退出点。

其实正好相反——结构化编程不鼓励多个入口点,但多个退出点是可以接受的,甚至是推荐的(比如“保护性语句”)。

8

我们是不是忘了为什么“多个退出点”最开始被认为是有害的呢?在过去(在大家普遍没有好的异常处理和 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

所以我们要确保讲清楚整个故事,之所以可以抛弃这个老问题,是因为新的编码实践让它变得不再必要。

撰写回答