如果您考虑一下,除了变量参数解包会同时解包的事实外,还有一个事实是format
不一定按顺序排列其参数,如'{2} {1} {0}'
.
如果format
只是采用一个序列而不是需要单独的参数,则可以通过构建一个做正确事情的序列来解决这个问题。这是一个简单的例子:
class DefaultList(list):
def __getitem__(self, idx):
try:
return super(DefaultList, self).__getitem__(idx)
except IndexError:
return '-'
当然,您的真实版本将包装任意可迭代的,而不是 subclass list
,并且可能必须使用tee
内部缓存并根据要求提取新值,只有在您通过结束时才会默认。(您可能想在 ActiveState 中搜索“惰性列表”或“惰性序列”配方,因为其中有一些这样做。)但这足以显示示例。
现在,这对我们有什么帮助?它没有;*lst
on a DefaultList
will 只是尝试从事物中创建一个元组,为我们提供与我们已经拥有的完全相同数量的参数。但是,如果你有一个版本format
可以只取一个 args 序列呢?然后你可以通过你的DefaultList
,它会工作的。
你确实有这个:Formatter.vformat
。
>>> string.Formatter().vformat('{0} {1} {2}', DefaultList([0, 1]), {})
'0 1 -'
但是,有一种更简单的方法,一旦您Formatter
通过该方法显式使用而不是隐式使用str
。您可以只覆盖它的get_value
方法和/或它的check_unused_args
:
class DefaultFormatter(string.Formatter):
def __init__(self, default):
self.default = default
# Allow excess arguments
def check_unused_args(self, used_args, args, kwargs):
pass
# Fill in missing arguments
def get_value(self, key, args, kwargs):
try:
return super(DefaultFormatter, self).get_value(key, args, kwargs)
except IndexError:
return '-'
f = DefaultFormatter('-')
print(f.vformat('{0} {2}', [0], {}))
print(f.vformat('{0} {2}', [0, 1, 2, 3], {}))
当然,您仍然需要将迭代器包装在提供序列协议的东西中。
当我们这样做时,如果该语言具有“可迭代解包”协议,则可以更直接地解决您的问题。请参阅此处了解提出此类问题的 python-ideas 线程,以及该想法存在的所有问题。(还要注意这个format
函数会使这个变得更棘手,因为它必须直接使用解包协议而不是依赖解释器来神奇地完成它。但是,假设它这样做了,那么你只需要编写一个非常简单的以及围绕任何处理它的迭代的通用包装器__unpack__
。)