生成器可以在python中与string.format一起使用吗?

时间:2013-08-27 17:57:11

标签: python string format generator

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

打印:

'1, 2, 3'

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

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

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

预期:

'1, 2, -'

但它永远不会回来。你能用发电机吗?有更好的方法吗?

4 个答案:

答案 0 :(得分:4)

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

Python迭代生成器以加载所有参数以传递给callable,如果生成器是无限的,那将永远不会完成。

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

from itertools import islice

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

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

答案 1 :(得分:3)

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

答案 2 :(得分:3)

如果你考虑一下,除了变量参数解包一次解包的事实之外,还有一个事实是format不一定按顺序接受它的参数,如'{2} {1} {0}'。 / p>

如果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], {}))

当然,你仍然需要将迭代器包装在提供Sequence协议的东西中。


虽然我们正在使用它,但如果语言具有“可迭代解包”协议,则可以更直接地解决您的问题。请参阅here以获取提出此类事情的python-ideas线程,以及该想法所具有的所有问题。 (另请注意,format函数会使这更棘手,因为它必须直接使用解包协议而不是依赖解释器来神奇地执行它。但是,假设它这样做,那么你只需要为任何处理__unpack__的迭代编写一个非常简单和通用的包装器。)

答案 3 :(得分: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)))