7
"{}, {}, {}".format(*(1,2,3,4,5))

印刷:

'1, 2, 3'

只要 in 的数量不超过元组的长度,这就是有效{}format。我想让它适用于任意长度的元组,-如果长度不足,则用 s 填充它。为了避免对 's 的数量做出假设{},我想使用生成器。这是我的想法:

def tup(*args):
    for s in itertools.chain(args, itertools.repeat('-')):
        yield s

print "{}, {}, {}".format(*tup(1,2))

预期的:

'1, 2, -'

但它永远不会回来。你能让它与发电机一起工作吗?有更好的方法吗?

4

4 回答 4

4

您不能使用无限生成器来填充任何 *args任意参数调用。

Python 遍历生成器以加载所有参数以传递给可调用对象,如果生成器是无限的,那将永远不会完成。

您可以毫无问题地使用非无尽的生成器。您可以使用itertools.islice()来限制生成器:

from itertools import islice

print "{}, {}, {}".format(*islice(tup(1,2), 3))

毕竟,您已经知道您的模板有多少个插槽。

于 2013-08-27T17:58:47.530 回答
4

如果您考虑一下,除了变量参数解包会同时解包的事实外,还有一个事实是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 中搜索“惰性列表”或“惰性序列”配方,因为其中有一些这样做。)但这足以显示示例。

现在,这对我们有什么帮助?它没有;*lston a DefaultListwill 只是尝试从事物中创建一个元组,为我们提供与我们已经拥有的完全相同数量的参数。但是,如果你有一个版本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__。)

于 2013-08-27T18:36:29.983 回答
3

Martijn Pieters 有直接的答案,但如果你想为自动填充创建某种通用包装器/帮助器format,你可以查看string.Formatter.parse. 使用它,您可以获得format格式字符串的表示形式,并去除参数计数/命名参数名称以动态确定迭代器需要多长时间。

于 2013-08-27T18:09:35.183 回答
1

天真的方法是向格式函数提供 L/2 参数,其中 L 是格式字符串的长度。由于替换令牌至少有 2 个字符长,因此您肯定总是有足够的值来解压:

def tup(l, *args):
    for s in args + (('-',) * l):
        yield s   
s = "{}, {}, {}"
print s.format(*list(tup(len(s)//2, 1, 2)))

正如 Silas Ray 所建议的,可以使用string.Formatter.parse找到更精细的上限

import string
def tup(l, *args):
    for s in args + (('-',) * l):
        yield s   
s = "{}, {}, {}"
l = len(list(string.Formatter().parse(s)))
print s.format(*list(tup(l, 1, 2)))
于 2013-08-27T18:28:29.057 回答