如何在不破坏遗留代码的情况下增加函数的返回值?

Posted

技术标签:

【中文标题】如何在不破坏遗留代码的情况下增加函数的返回值?【英文标题】:How to increase return values of functions without breaking legacy code? 【发布时间】:2018-08-04 02:20:53 【问题描述】:

例如,假设您有一个函数:

def foo(a): 
    return a

还有大量使用此功能的遗留代码:

amount = 4 + foo(a)

如果我需要在不破坏现有代码的情况下增加 foo 的返回值怎么办:

def foo(a)
   return a, a + 5

现在当我这样做时,变量 'amount' 不是正确答案,因为 a 现在是,用 pylint 的话来说是 'a tuple' 所以数量将从返回 a + 4 变为返回 (a, a+5) + 4

如何将 'a + 5' 添加到 foo,同时仍然允许数量是单个标量值,而不是元组?

【问题讨论】:

视实际情况而定。可以使用非占位符代码吗? 为什么你不能只在另一个函数中扭曲这个函数并在需要时调用该包装器并且它不会影响遗留代码? 【参考方案1】:

也许这样的事情会起作用:

def foo(a, new_usage=False):
    if not new_usage:
        return a

    return a + 5

然后在所有新的使用情况下像这样调用你的函数:

foo(a, True)

例如,您的旧代码仍然可以工作:

In [40]: amount = 4 + foo(4)

In [41]: amount
Out[41]: 8

对于新的用法,您可以这样做:

In [42]: amount = 4 + foo(4, True)

In [43]: amount
Out[43]: 13

【讨论】:

不知道为什么你被否决了——这似乎正是我会采取的路线。也许添加一个示例来说明foo(a) 如何仍将返回原始行为? 谢谢!这似乎可行,并且我认为足够优雅。我会接受答案 这通常是一件可怕的事情——但作为一个天生可怕问题的解决方案,这是不可避免的。我认为按照 juanpa 的建议创建一个单独的函数仍然要好得多,但在极少数情况下你不能这样做,这可能是最好的选择。 你解决了问题,但又制造了另一个问题;你只是炸毁了封装(OOP)。每当您需要进行更改时,您只需修改您的原始实现!? 我刚刚发布了我认为更优雅的解决方案(尽管晚了 2 天)。如果您不介意,请查看。【参考方案2】:

一个更优雅的解决方案是让foo 成为扩展int(或float,取决于您的旧版foo() 的输出)的类,同时实现__iter__ 方法以允许它在序列上下文中使用时被解包为序列:

class foo(int):
    def __iter__(self):
        return iter((self, self + 5))

这样:

amount = 4 + foo(1) # used as an int
print(amount)
a, b = foo(1) # used in a sequence context so foo.__iter__ is called
print(a, b)

会输出:

5
1 6

但是,当然,上面的代码只是因为在你的问题中,foo(a) 只是返回a,而这恰好是int 构造函数的行为,所以通过扩展int,@987654333 @也方便返回a

如果您的 foo(a) 实际上在您的生产遗留代码中返回了除 a 之外的任何内容,那么您将需要覆盖 __new__ 方法,以便它返回一个不同值的新 int 实例。

如果您的旧版foo(a) 返回a + 1,则下面显示了一个示例:

class foo(int):
    @classmethod
    def __new__(cls, type, a):
        return super(foo, cls).__new__(type, a + 1)
    def __iter__(self):
        return iter((self, self + 5))

这样:

amount = 4 + foo(1)
print(amount)
a, b = foo(1)
print(a, b)

会输出:

6
2 7

【讨论】:

【参考方案3】:

重命名函数并添加新的返回值。 然后编写一个像原始函数一样命名的包装器,它会提取遗留的返回值:

原码:

def foo(x):
    return a

新代码:

def enhanced_foo(x):
    return a, extra_info

def foo(x):
    return enhanced_foo(x)[0]

遗留代码仍然有效:

a = foo(x)

新代码使用新名称:

a, extra = enhanced_foo(x)

【讨论】:

以上是关于如何在不破坏遗留代码的情况下增加函数的返回值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不破坏封装的情况下返回对 RefCell 内某些内容的引用?

在函数中,如何在不使用 ref 游标的情况下返回多个值?

如何在不使用闭包的情况下快速执行SelectorOnMainThread 并获取返回值?

如何在不使用集合的情况下返回多个值?

如何在不返回值的情况下显示消息

尝试在不指定返回参数的情况下调用存储的函数“dbname”。“functionname”