可迭代解包的默认值

时间:2017-06-18 18:46:31

标签: python python-3.x iterable-unpacking

我经常因为Python的可迭代解包缺乏灵活性而感到沮丧。

采用以下示例:

a, b = range(2)

工作正常。正如预期的那样,a包含0b包含1。现在让我们试试这个:

a, b = range(1)

现在,我们得到一个ValueError

ValueError: not enough values to unpack (expected 2, got 1)

如果期望的结果是0中的aNone中的b,则不理想。

有很多黑客可以解决这个问题。我见过的最优雅的是:

a, *b = function_with_variable_number_of_return_values()
b = b[0] if b else None

不漂亮,可能会让Python新手感到困惑。

那么最恐怖的方式是什么?将返回值存储在变量中并使用if块? *varname黑客?还有别的吗?

2 个答案:

答案 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)