【问题标题】:Dynamic string formatting in pythonpython中的动态字符串格式化
【发布时间】:2014-10-11 05:23:23
【问题描述】:

我想按如下方式打印我的输出。虽然乍一看似乎很容易完成,但问题就在这里。列数可以变化,可以是负数、不同位数的数字等。

那么我怎样才能完美地做到这一点,以使外部边界保持完整,而不管内部的各种因素如何。

我知道我必须在 python 中使用字符串格式,但不知道该怎么做。
1.打印表头+---+应该用什么逻辑?
如何根据内部元素的数量来确定长度?
2. 如何打印固定宽度和对齐的数字?

+---------------------------+  
|  3  4  -4   -8  -10  -12  |  
|  5  5   3   -3   -4  -44  |  
| 34 -4 -34  -22   22   22  |
+---------------------------+  

编辑

我想使用字符串格式来实现这一点,而不是借助模块。

【问题讨论】:

  • 我现在没有时间给出完整的答案,但一般的方法是: 1. 一次构建一列,在每一步重新调整字符串长度; 2. 围绕它构建盒子。

标签: python string-formatting


【解决方案1】:

不使用包和模块:

nums= [[  3, 4, -4,  -8, -10, -12,], [  5, 5,  3,  -3,  -4, -44,], [ 34,-4,-34, -22,  22,  22]]

t = ['|' + ''.join('%4i' % i for i in row) + ' |' for row in nums]
hdr = '+' +  (len(t[0])-2) * '-' + '+'
print '\n'.join( [hdr] + t + [hdr] )

这会产生输出:

+-------------------------+
|   3   4  -4  -8 -10 -12 |
|   5   5   3  -3  -4 -44 |
|  34  -4 -34 -22  22  22 |
+-------------------------+

它是如何工作的:

  • t = ['|' + ''.join('%4i' % i for i in row) + ' |' for row in nums]

    t 包含除顶行和底行之外的所有内容。在其核心,数字被格式化为固定宽度并根据规范%4i 对齐。 %4i 表示允许四个空格,格式为整数,右对齐。许多其他规格是可能的。例如,如果您想将 5 个空格宽的整数左对齐,请使用 %-5i

  • hdr = '+' + (len(t[0])-2) * '-' + '+'

    现在内部行保存在t 中,我们可以组装标题行和尾行。这些行以加号开始和结束。其余的都是-

  • print '\n'.join( [hdr] + t + [hdr] )

    这会将hdr 字符串添加到行列表t 的开头和结尾,然后将所有行与换行符连接在一起以形成最终表格。

更复杂的例子

让我们格式化上表,但在每行的末尾添加每行的最小值、最大值、平均值和标准差。

def mmmsd(row):
    mean=sum(row)/len(row)
    stddev = ( sum( (x-mean)**2.0 for x in row ) / float(len(row)) )**0.5
    return '%6i%6i%6.2f%6.2f' % (min(row), max(row), mean, stddev)

nums= [[  3, 4, -4,  -8, -10, -12,], [  5, 5,  3,  -3,  -4, -44,], [ 34,-4,-34, -22,  22,  22]]

stats = [mmmsd(row) for row in nums]
t = [10*' ' + '|' + ''.join('%6i' % i for i in row) + ' |' + st for row, st in zip(nums, stats)]
hdr = 10*' ' + '+' +  (len(t[0])-12 - len(stats[0])) * '-' + '+' + len(stats[0]) * ' '
print '\n'.join( [hdr] + t + [hdr] )

这会产生结果:

          +-------------------------------------+
          |     3     4    -4    -8   -10   -12 |   -12     4 -5.00  6.18
          |     5     5     3    -3    -4   -44 |   -44     5 -7.00 17.23
          |    34    -4   -34   -22    22    22 |   -34    34  3.00 24.92
          +-------------------------------------+                        

【讨论】:

  • join 语句中方括号的相关性是什么? [hdr]
  • hdr 是一个字符串。 t 是一个列表。要将它们加在一起,我们需要将 hdr 转换为列表。这就是[hdr] 中方括号的作用。 [hdr] + t + [hdr] 的结果是一个新列表,其第一个和最后一个元素是 hdr。
  • (len(t[0])-2) * 这似乎不对。这是如何计算的?
  • @cppcoder len(t[0])t 第一行的列数,与最终输出的列数相同。对于标题,第一个和最后一个字符是+。因此,我们希望在这两个加号之间添加(len(t[0])-2) 破折号。
  • @cppcoder 我们从nums 中的数字开始。 t 中的行是字符串,而不是数字。它们已经完全格式化,包括前导和尾随垂直条。因此,len(t[0]) 是字符输出的总列数,(len(t[0])-2) 是所需的破折号总数。
【解决方案2】:

这就是我喜欢 Python 的地方——总有一些东西阻止你重新发明轮子。

对于您的用例,prettytable 非常适合:

import prettytable

l = [
    [3, 4, -4, -8, -10, -12],
    [5, 5, 3, -3, -4, -44],
    [34, -4, -34, -22, 22, 22]
]

table = prettytable.PrettyTable(header=False, vrules=prettytable.FRAME)
for row in l:
    table.add_row(row)

print table

打印:

+----+----+-----+-----+-----+-----+
| 3  | 4  |  -4 |  -8 | -10 | -12 |
| 5  | 5  |  3  |  -3 |  -4 | -44 |
| 34 | -4 | -34 | -22 |  22 |  22 |
+----+----+-----+-----+-----+-----+

同时检查包文档页面的Manually changing table style 段落。


也有texttable,但在调整表格外观和感觉方面功能较弱:

import texttable

l = [
    [3, 4, -4, -8, -10, -12],
    [5, 5, 3, -3, -4, -44],
    [34, -4, -34, -22, 22, 22]
]

table = texttable.Texttable()
table.add_rows(l, header=False)

print table.draw()

打印:

+----+----+-----+-----+-----+-----+
| 3  | 4  | -4  | -8  | -10 | -12 |
+----+----+-----+-----+-----+-----+
| 5  | 5  | 3   | -3  | -4  | -44 |
+----+----+-----+-----+-----+-----+
| 34 | -4 | -34 | -22 | 22  | 22  |
+----+----+-----+-----+-----+-----+

另一个选项是tabulate,它引入了一组预定义的表格格式,例如“网格”:

from tabulate import tabulate

l = [
    [3, 4, -4, -8, -10, -12],
    [5, 5, 3, -3, -4, -44],
    [34, -4, -34, -22, 22, 22]
]

table = tabulate(l, tablefmt="grid")
print table

打印:

+----+----+-----+-----+-----+-----+
|  3 |  4 |  -4 |  -8 | -10 | -12 |
+----+----+-----+-----+-----+-----+
|  5 |  5 |   3 |  -3 |  -4 | -44 |
+----+----+-----+-----+-----+-----+
| 34 | -4 | -34 | -22 |  22 |  22 |
+----+----+-----+-----+-----+-----+

另见相关主题:

【讨论】:

  • 如何在不借助本模块的情况下使用字符串格式来实现这一点?
【解决方案3】:

1 首先你必须在打印之前计算数字 -

2format print in python

【讨论】:

    【解决方案4】:

    此变体计算打印前所需的最大列宽,因此所有列的宽度相同,但您无需将宽度硬编码到打印格式字符串中。它使用方便但相对晦涩的* 格式参数来确定字段宽度。

    >>> nums=[[3, 4, -4, -8, -10, -12], [5, 5, 3, -3, -4, -44], [34, -4, -34, -22, 22, 22]]
    >>> width=max(len(str(i)) for i in sum(nums,[]))
    >>> body='\n'.join(['| ' + ' '.join(['%*d' % (width, i) for i in row]) + ' |' for row in nums])
    >>> cols=len(nums[0]); hdr='+' + (cols * (width + 1) + 1) * '-' + '+'
    >>> print '%s\n%s\n%s' % (hdr, body, hdr)
    +-------------------------+
    |   3   4  -4  -8 -10 -12 |
    |   5   5   3  -3  -4 -44 |
    |  34  -4 -34 -22  22  22 |
    +-------------------------+
    

    【讨论】:

      【解决方案5】:

      另一个变种是使用 str.format() 方法。

      def extract_items_from_nested_list (_list):
          '''
          Generator extracts items of nested lists
          '''
          for item in _list: 
              if isinstance(item, list):
                  yield from extract_items_from_nested_list(item)
              else:
                  yield item
      
      def _evaluate_column_width (nested_int_list, extra_space=1):
          '''
          Helper function to evaluate required width of the columns
          '''
          flat_value_list = list(extract_items_from_nested_list(nested_int_list))
          # evaluate size of string length for every item
          string_size_list = [len(str(item)) for item in flat_value_list]
          # evaluate max size and add some extra space
          return max(string_size_list) + extra_space
      
      def render_table(nums):
          column_width = _evaluate_column_width(nums)
          # using str.format()
          format_string = "{:" + str(column_width) + "d}"
          t = ['|' + ''.join(format_string.format(value) for value in row) + ' |' for row in nums]
          hdr = '+' + (len(t[0]) - 2) * '-' + '+'
          print('\n'.join([hdr] + t + [hdr]))
      
      if __name__ == '__main__':
          nums= [[  3, 4, -4,  -8, -10, -12], [  5, 5,  3,  -3,  -4, -44,], [ 34,-4,-34, -22,  22,  22]]
          render_table(nums)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-17
        • 2010-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多