在python中返回多个值而不破坏以前的代码

时间:2012-03-27 11:45:33

标签: python paradigms

我有一个类似函数的版本1:

def f(a,b,c):
    #Some processing
    return some_result

稍后,我将其升级到版本2.

def f(a,b,c):
    #Some processing
    #Some more processing
    return some_result, additional_result

后一版本返回一个元组。因此,使用版本1的所有客户端代码都已过时。我可以按需获得additional_result 吗?

只有在您继续获得additional_result并且没有任何改变时,您才会获得some_result

我想到的一个技巧:

def f(a,b,c, need_additional = False):
    #Some processing
    #Some more processing
    if not need_addional:
        return some_result
    else:
        return some_result, additional_result

还有什么更好的?还是更通用的?

7 个答案:

答案 0 :(得分:9)

我认为更优雅的解决方案是让您的旧功能成为新功能的传统包装器:

def f(a,b,c):
    some_result, additional_result = f_new(a,b,c)
    return some_result

def f_new(a,b,c):
    #Some processing
    #Some more processing
    return some_result, additional_result

我必须承认我主要使用你建议的模式,而不是我的:),但是向后兼容的默认参数并不是很好的练习。

答案 1 :(得分:2)

additional_result添加为some_result

的字段

如果需要,将some_result更改为继承其数据类应该是什么的类,但使用additional_result。 (所以遗留代码可以简单地忽略你的“额外位”)

甚至更好。

只需创建返回多个值的单独函数即可。 亲吻;)

答案 2 :(得分:2)

所提供的解决方案都不是完美的。

  1. 基于标记(提问者的提议)的灵活回报很难维护,并打破任何明确的API结构。
  2. Legacy wrappers(Dvir Volk的回答)将为您的API添加越来越多的功能,从而让它成长。它也没有盖上东西;对于任何新的额外回报值,您将再次执行此操作。
  3. 只有在您可以控制结果的创建时,才能进行结果增强(Przemo Li的回答)。如果你想返回一些库为你创建的值,你所能做的只是在它周围放一个包装器(一旦结果类因库更新或类似而改变就会出现问题)。
  4. 对所有使用代码进行新的API进行简化和重构并不总是一种选择,例如: G。如果您的客户使用代码维护它。
  5. 其他功能(也在Przemo Li的回答和声明的KISS中)也会混乱你的API。
  6. 实际上,虽然看起来确实很难看,但我想我更喜欢已经提到的通过out-parameters传递额外结果的方法。我的编码方式是:

    老来电者:

    result = f(a, b, c)
    print result
    

    新来电者:

    additionalResult = [0]
    result = f(a, b, c, additionalResult)
    print result, additionalResult[0]
    

    被叫方:

    def f(a, b, c, additionalResult=[None]):
        #Some processing
        #Some more processing
        additionalResult[0] = someValue
        return some_result
    

    这也是很好的,经常在C中练习,所以有充分的理由相信这不会是我们可能忽略的陷阱,虽然它可能看起来很难看。

答案 3 :(得分:1)

虽然我不是Python专家,但我会以你提到的方式实现它,在我看来,这是一个非常直接的改变,以便允许向后兼容。

答案 4 :(得分:1)

如果您不必在同一文件中同时使用这两个版本,则可以通过包含该方法的2个版本的模块来控制它,然后仅在调用模块中导入所需的版本。然后,当你最终完成并重构旧代码时,你所要做的就是在完成后改变import语句。

或者,在同一模块中使用from x import y as z构造和两种实现。

答案 5 :(得分:0)

您还可以在字典中返回额外值作为引用,其引用作为额外参数传递,例如:

def f(a,b,c, additional_result_dict = {}):
    #Some processing
    #Some more processing
    additional_result_dict['additional_result'] = additional_result
    return some_result

答案 6 :(得分:0)

我终于实现了它:

def f(a,b,c, v2 = False):
    #Some processing
    #Some more processing
    if not v2:
        return some_result
    else:
        v2_dict = {}
        v2_dict['additional_result'] = additional_result
        v2_dict['...'] = ...
        return some_result, v2_dict

<强>优点:

  • 旧版代码不会中断。
  • 未来版本中的返回值没有限制。
  • 代码是可管理的。