Matlab的python中的矢量化sprintf函数

时间:2016-02-13 11:45:19

标签: python matlab printf

在使用Matlab一段时间后,我非常喜欢它的sprintf函数,它是矢量化的(矢量化是问题的关键部分)。

假设有一个列表li=[1,2,3,4,5,6]

sprintf("%d %d %d\n", li)

将在li中逐个返回

的元素应用格式
"1 2 3\n4 5 6\n" 

作为字符串。

我目前的解决方案并不像pythonic那样:

def my_sprintf(formatstr, args):

    #number of arguments for format string:
    n=formatstr.count('%')

    res=""

    #if there are k*n+m elements in the list, leave the last m out
    for i in range(n,len(args)+1,n):
        res+=formatstr%tuple(args[i-n:i])

    return res

在python中执行此操作的常用/更好方法是什么?

是否有可能,没有明确地从格式字符串中引出预期参数的数量(n=formatstr.count('%')感觉就像一个黑客攻击)?

PS:为了简单起见,可以假设列表中的元素数量是格式字符串中参数数量的倍数。

2 个答案:

答案 0 :(得分:2)

如果让用户传入块大小,您可以使用grouper recipe的变体。

def sprintf(iterable,fmt, n):
    args = zip(*[iter(iterable)] * n)
    return "".join([fmt % t for t in args])

输出:

In [144]: sprintf(li,"%.2f %.2f %d\n", 3)
Out[144]: '1.00 2.00 3\n4.00 5.00 6\n'

In [145]: sprintf(li,"%d %d %d\n", 3)
Out[145]: '1 2 3\n4 5 6\n'

您可以使用izip_longest和str.format处理块大小不是列表大小的倍数但是它不允许您指定类型而不会出错:

from itertools import izip_longest


def sprintf(iterable, fmt, n, fillvalue=""):
    args = izip_longest(*[iter(iterable)] * n, fillvalue=fillvalue)
    return "".join([fmt.format(*t) for t in args])

如果您拆分占位符或让用户传递可迭代的占位符,您可以捕获所有潜在问题。

 def sprintf(iterable, fmt, sep=" "):
    obj = object()
    args = izip_longest(*[iter(iterable)] * len(fmt), fillvalue=obj)
    return "".join(["{sep}".join([f % i for f, i in zip(fmt, t) if i is not obj]).format(sep=sep) + "\n"
                    for t in args])

演示:

In [165]: sprintf(li, ["%.2f", "%d", "%.2f", "%2.f"])
Out[165]: '1.00 2 3.00  4\n5.00 6\n'

In [166]: sprintf(li, ["%d", "%d", "%d"])
Out[166]: '1 2 3\n4 5 6\n'

In [167]: sprintf(li, ["%f", "%f", "%.4f"])
Out[167]: '1.000000 2.000000 3.0000\n4.000000 5.000000 6.0000\n'

In [168]: sprintf(li, ["%.2f", "%d", "%.2f", "%2.f"])
Out[168]: '1.00 2 3.00  4\n5.00 6\n'

答案 1 :(得分:0)

您可能希望删除for循环中的+=。以下版本大约比您的快三倍。即使您想要在输出中打印%符号,它也可以工作。因此,格式字符串包含'%%'

def my_sprintf(format_str, li):

    n = format_str.count('%') - 2*format_str.count('%%')
    repeats = len(li)//n

    return (format_str*repeats) % tuple(li[:repeats*n])

如果您使用较新的.format方法而不是%,则可能会采用不太苛刻的方法。在这种情况下,您可以使用string.Formatter().parse()方法获取format_str中使用的字段列表。

该功能如下所示:

import string

li = [1, 2, 3, 4, 5, 6, 7]
format_str = '{:d} {:d} {:d}\n'


def my_sprintf(format_str, li):

    formatter = string.Formatter()
    n = len(list(filter(lambda a: a[2] is not None, 
                        formatter.parse(format_str))))

    repeats = len(li)//n
    return (format_str*repeats).format(*li[:repeats*n])