我经常因为Python的可迭代解包缺乏灵活性而感到沮丧。
采用以下示例:
a, b = range(2)
工作正常。正如预期的那样,a
包含0
和b
包含1
。现在让我们试试这个:
a, b = range(1)
现在,我们得到一个ValueError
:
ValueError: not enough values to unpack (expected 2, got 1)
如果期望的结果是0
中的a
和None
中的b
,则不理想。
有很多黑客可以解决这个问题。我见过的最优雅的是:
a, *b = function_with_variable_number_of_return_values()
b = b[0] if b else None
不漂亮,可能会让Python新手感到困惑。
那么最恐怖的方式是什么?将返回值存储在变量中并使用if块? *varname
黑客?还有别的吗?
答案 0 :(得分:1)
正如评论中所提到的,最好的方法是简单地让你的函数返回一个常量的值,如果你的用例实际上更复杂(比如参数解析),请使用它。
然而,你的问题明确要求Pythonic处理返回可变数量参数的函数的方法,我相信它可以用decorators干净地完成。它们并不是非常常见,大多数人倾向于使用它们而不是创建它们,所以在这里down-to-earth tutorial on creating decorators可以了解它们。
以下是一个装饰功能,可以完成您正在寻找的功能。该函数返回一个带有可变数量参数的迭代器,并将其填充到一定长度,以便更好地适应迭代器解包。
def variable_return(max_values, default=None):
# This decorator is somewhat more complicated because the decorator
# itself needs to take arguments.
def decorator(f):
def wrapper(*args, **kwargs):
actual_values = f(*args, **kwargs)
try:
# This will fail if `actual_values` is a single value.
# Such as a single integer or just `None`.
actual_values = list(actual_values)
except:
actual_values = [actual_values]
extra = [default] * (max_values - len(actual_values))
actual_values.extend(extra)
return actual_values
return wrapper
return decorator
@variable_return(max_values=3)
# This would be a function that actually does something.
# It should not return more values than `max_values`.
def ret_n(n):
return list(range(n))
a, b, c = ret_n(1)
print(a, b, c)
a, b, c = ret_n(2)
print(a, b, c)
a, b, c = ret_n(3)
print(a, b, c)
输出您正在寻找的内容:
0 None None
0 1 None
0 1 2
装饰器基本上接受装饰函数并返回其输出以及足够的额外值以填充max_values
。然后调用者可以假设函数总是返回max_values
个参数,并且可以像平常一样使用花哨的解包。
答案 1 :(得分:0)
以下是@ supersam654的装饰器解决方案的替代版本,使用迭代器而不是效率列表:
def variable_return(max_values, default=None):
def decorator(f):
def wrapper(*args, **kwargs):
actual_values = f(*args, **kwargs)
try:
for count, value in enumerate(actual_values, 1):
yield value
except TypeError:
count = 1
yield actual_values
yield from [default] * (max_values - count)
return wrapper
return decorator
它的使用方式相同:
@variable_return(3)
def ret_n(n):
return tuple(range(n))
a, b, c = ret_n(2)
这也可以用于非用户定义的函数,如:
a, b, c = variable_return(3)(range)(2)