可迭代拆包的默认值
Default values for iterable unpacking
我经常因 Python 的可迭代拆包缺乏灵活性而感到沮丧。
举个例子:
a, b = range(2)
工作正常。正如预期的那样,a
包含 0
,b
包含 1
。现在让我们试试这个:
a, b = range(1)
现在,我们得到 ValueError
:
ValueError: not enough values to unpack (expected 2, got 1)
不理想,当期望的结果是 a
中的 0
和 b
中的 None
。
有很多技巧可以解决这个问题。我见过的最优雅的是:
a, *b = function_with_variable_number_of_return_values()
b = b[0] if b else None
不漂亮,可能会让 Python 新手感到困惑。
那么最 Pythonic 的方法是什么?将 return 值存储在变量中并使用 if 块? *varname
黑客攻击?还有别的吗?
如评论中所述,最好的方法是让函数 return 具有恒定数量的值,如果您的用例实际上更复杂(如参数解析),请使用它的图书馆。
但是,您的问题明确要求使用 Pythonic 方法来处理 return 可变数量参数的函数,我相信它可以用 decorators. They're not super common and most people tend to use them more than create them so here's a down-to-earth tutorial on creating decorators 了解更多关于他们的信息。
下面是一个修饰函数,可以满足您的需求。函数 return 是一个具有可变数量参数的迭代器,它被填充到一定长度以更好地适应迭代器解包。
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
装饰器基本上采用装饰函数和 return 其输出以及足够的额外值来填充 max_values
。然后调用者可以假定该函数总是 return 恰好 max_values
个参数,并且可以像平常一样使用花式解包。
这是@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)
我经常因 Python 的可迭代拆包缺乏灵活性而感到沮丧。
举个例子:
a, b = range(2)
工作正常。正如预期的那样,a
包含 0
,b
包含 1
。现在让我们试试这个:
a, b = range(1)
现在,我们得到 ValueError
:
ValueError: not enough values to unpack (expected 2, got 1)
不理想,当期望的结果是 a
中的 0
和 b
中的 None
。
有很多技巧可以解决这个问题。我见过的最优雅的是:
a, *b = function_with_variable_number_of_return_values()
b = b[0] if b else None
不漂亮,可能会让 Python 新手感到困惑。
那么最 Pythonic 的方法是什么?将 return 值存储在变量中并使用 if 块? *varname
黑客攻击?还有别的吗?
如评论中所述,最好的方法是让函数 return 具有恒定数量的值,如果您的用例实际上更复杂(如参数解析),请使用它的图书馆。
但是,您的问题明确要求使用 Pythonic 方法来处理 return 可变数量参数的函数,我相信它可以用 decorators. They're not super common and most people tend to use them more than create them so here's a down-to-earth tutorial on creating decorators 了解更多关于他们的信息。
下面是一个修饰函数,可以满足您的需求。函数 return 是一个具有可变数量参数的迭代器,它被填充到一定长度以更好地适应迭代器解包。
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
装饰器基本上采用装饰函数和 return 其输出以及足够的额外值来填充 max_values
。然后调用者可以假定该函数总是 return 恰好 max_values
个参数,并且可以像平常一样使用花式解包。
这是@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)