当我们需要比以前多 return 个值时,保持函数的向后兼容性
Keep backward compatibility for a function when we need to return more values than before
我有一个函数,当前 return 有两个值,一个 int 和一个字符串,例如:
def myfunc():
return 0, 'stringA'
此函数已在很多代码中使用,但我需要对其进行改进,使其 return 具有三个值,一个 int 和两个字符串,例如:
def myfunc():
return 0, 'stringA', 'stringB'
当然,我想保持与现有代码的兼容性,所以 return像上面修改的函数那样输入值将导致 ValueError。
一种解决方案是将改进后的函数包装到另一个具有旧名称的函数中,因此我们在现有代码中调用初始函数,在新代码中调用新函数,例如:
def newmyfunc():
return 0, 'A', 'B'
def myfunc():
result1, result2, _ = newmyfunc()
return result1, result2
就此解决方案而言,我并不觉得它很优雅。
有没有更好的方法来实现这个目标?
类似于可以 return 两个或三个值而无需修改使用该函数的现有代码的多态函数?
首先,回答一个你没有问过但可能对将来或对其他人有帮助的问题:
当我发现我从单个函数 returning 多个项目时,尤其是当项目列表 returned 开始增长时,我经常发现 return 字典或对象而不是元组。原因是随着 returned-item 列表的增长,跟踪哪个项目在哪个索引处变得更加困难。如果一组 returned 项目将被单独使用并且不是 closely-related 除了来自同一个函数之外,我更喜欢 dict。如果 returned 项 在多个位置一起使用(例如用户名、密码、主机和端口),将它们全部包装在一个对象中(实例化一个自定义 class),然后传递它。 YMMV,听起来你在试图避免重构代码,所以:
解决您的实际问题的最简单方法是向您的函数添加关键字参数,为该参数设置默认值,并使用它来决定 return:[=12= 的参数版本]
def myfunc(return_length=2):
if return_length == 2:
return 0, 'stringA'
elif return_length == 3:
return 0, 'stringA', 'stringB'
else:
raise ValueError(f'Unexpected number of return arguments {return_length}')
旧代码继续调用函数 as-is,新代码显式调用 my_func(return_length=3)
。当所有旧代码都被弃用时,您可以将默认值更改为 3 and/or 将其设置为 2 时抛出错误。
装饰器示例:所涉及函数的主体保持不变,“修改”部分委托给外部函数,装饰器。
假设“基础函数”不带参数。
def dec(f_reference):
return lambda f_extra_feature: lambda:(*f_reference(), f_extra_feature())
def myfunc():
return 0, 'stringA'
def xxxfunc():
return 'XXX'
myfunc = dec(f_reference=myfunc)(f_extra_feature=xxxfunc)
print(myfunc)
#(0, 'stringA', 'XXX')
根据需要,第二个参数 f_extra_feature
可以隐式设置。
可以使用语法糖表示法来完成不太灵活的修饰
# order of argument is changed!
def dec2(f_extra_feature):
return lambda f_reference: lambda:(*f_reference(), f_extra_feature())
def xxxfunc():
return 'XXX'
@dec2(f_extra_feature=xxxfunc)
def myfunc():
return 0, 'stringA'
print(myfunc())
#(0, 'stringA', 'XXX')
编辑:
def newmyfunc():
return 0, 'A', 'B'
def replacer(f):
return lambda f_target: lambda: f()[slice(0, 2)]
@replacer(newmyfunc)
def myfunc():
return 0, 'stringA'
# new body of the function, execute newmyfunc
print(myfunc())
我有一个函数,当前 return 有两个值,一个 int 和一个字符串,例如:
def myfunc():
return 0, 'stringA'
此函数已在很多代码中使用,但我需要对其进行改进,使其 return 具有三个值,一个 int 和两个字符串,例如:
def myfunc():
return 0, 'stringA', 'stringB'
当然,我想保持与现有代码的兼容性,所以 return像上面修改的函数那样输入值将导致 ValueError。
一种解决方案是将改进后的函数包装到另一个具有旧名称的函数中,因此我们在现有代码中调用初始函数,在新代码中调用新函数,例如:
def newmyfunc():
return 0, 'A', 'B'
def myfunc():
result1, result2, _ = newmyfunc()
return result1, result2
就此解决方案而言,我并不觉得它很优雅。
有没有更好的方法来实现这个目标? 类似于可以 return 两个或三个值而无需修改使用该函数的现有代码的多态函数?
首先,回答一个你没有问过但可能对将来或对其他人有帮助的问题:
当我发现我从单个函数 returning 多个项目时,尤其是当项目列表 returned 开始增长时,我经常发现 return 字典或对象而不是元组。原因是随着 returned-item 列表的增长,跟踪哪个项目在哪个索引处变得更加困难。如果一组 returned 项目将被单独使用并且不是 closely-related 除了来自同一个函数之外,我更喜欢 dict。如果 returned 项 在多个位置一起使用(例如用户名、密码、主机和端口),将它们全部包装在一个对象中(实例化一个自定义 class),然后传递它。 YMMV,听起来你在试图避免重构代码,所以:
解决您的实际问题的最简单方法是向您的函数添加关键字参数,为该参数设置默认值,并使用它来决定 return:[=12= 的参数版本]
def myfunc(return_length=2):
if return_length == 2:
return 0, 'stringA'
elif return_length == 3:
return 0, 'stringA', 'stringB'
else:
raise ValueError(f'Unexpected number of return arguments {return_length}')
旧代码继续调用函数 as-is,新代码显式调用 my_func(return_length=3)
。当所有旧代码都被弃用时,您可以将默认值更改为 3 and/or 将其设置为 2 时抛出错误。
装饰器示例:所涉及函数的主体保持不变,“修改”部分委托给外部函数,装饰器。
假设“基础函数”不带参数。
def dec(f_reference):
return lambda f_extra_feature: lambda:(*f_reference(), f_extra_feature())
def myfunc():
return 0, 'stringA'
def xxxfunc():
return 'XXX'
myfunc = dec(f_reference=myfunc)(f_extra_feature=xxxfunc)
print(myfunc)
#(0, 'stringA', 'XXX')
根据需要,第二个参数 f_extra_feature
可以隐式设置。
可以使用语法糖表示法来完成不太灵活的修饰
# order of argument is changed!
def dec2(f_extra_feature):
return lambda f_reference: lambda:(*f_reference(), f_extra_feature())
def xxxfunc():
return 'XXX'
@dec2(f_extra_feature=xxxfunc)
def myfunc():
return 0, 'stringA'
print(myfunc())
#(0, 'stringA', 'XXX')
编辑:
def newmyfunc():
return 0, 'A', 'B'
def replacer(f):
return lambda f_target: lambda: f()[slice(0, 2)]
@replacer(newmyfunc)
def myfunc():
return 0, 'stringA'
# new body of the function, execute newmyfunc
print(myfunc())