如果你想一想,除了变量参数解包会同时解包的事实之外,还有一个事实是format 不一定按顺序排列它的参数,就像在'{2} {1} {0}' 中一样。
如果format 只是采用一个序列而不是需要单独的参数,您可以通过构建一个执行正确操作的序列来解决此问题。这是一个简单的例子:
class DefaultList(list):
def __getitem__(self, idx):
try:
return super(DefaultList, self).__getitem__(idx)
except IndexError:
return '-'
当然,您的实际版本会包装任意可迭代,而不是子类 list,并且可能必须使用 tee 或内部缓存并根据请求拉入新值,仅在您通过时默认结束。 (您可能想在 ActiveState 中搜索“惰性列表”或“惰性序列”配方,因为其中有一些这样做。)但这足以显示示例。
现在,这对我们有什么帮助?它没有; *lst 在 DefaultList 上只会尝试从事物中创建一个元组,从而为我们提供与我们已经拥有的完全相同数量的参数。但是如果你有一个 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], {}))
当然,您仍然需要将迭代器包装在提供序列协议的东西中。
虽然我们这样做了,但如果该语言具有“可迭代解包”协议,则可以更直接地解决您的问题。请参阅here 以了解提出此类问题的 python-ideas 线程,以及该想法存在的所有问题。 (另请注意,format 函数会使这个变得更棘手,因为它必须直接使用解包协议而不是依赖解释器来神奇地完成它。但是,假设它这样做了,那么你只需要编写一个非常简单且通用的包装器,用于处理任何处理 __unpack__ 的可迭代对象。)