在python中创建漂亮的列输出

在python中创建漂亮的列输出,python,string,list,Python,String,List,我正在尝试用python创建一个很好的列列表,用于我创建的命令行管理工具 基本上,我想要一个如下列表: [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']] 变成: a b c aaaaaaaaaa b c a bbbbbbbbbb c 在这里,使用普通制表符是行不通的,因为我不知道每行中最长的数据 这与Linu

我正在尝试用python创建一个很好的列列表,用于我创建的命令行管理工具

基本上,我想要一个如下列表:

[['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
变成:

a            b            c
aaaaaaaaaa   b            c
a            bbbbbbbbbb   c
在这里,使用普通制表符是行不通的,因为我不知道每行中最长的数据

这与Linux中的“column-t”行为相同

$ echo -e "a b c\naaaaaaaaaa b c\na bbbbbbbbbb c"
a b c
aaaaaaaaaa b c
a bbbbbbbbbb c

$ echo -e "a b c\naaaaaaaaaa b c\na bbbbbbbbbb c" | column -t
a           b           c
aaaaaaaaaa  b           c
a           bbbbbbbbbb  c

我到处寻找各种python库来实现这一点,但找不到任何有用的东西

您必须通过两次传球来完成此操作:

  • 获取每列的最大宽度
  • 使用我们从第一遍开始的最大宽度知识格式化列,使用
    str.ljust()
    str.rjust()

  • 这样做的目的是计算最长的数据项以确定列宽,然后在打印每列时使用
    .ljust()
    添加必要的填充。

    像这样变换列是zip的工作:

    >>> a = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
    >>> list(zip(*a))
    [('a', 'aaaaaaaaaa', 'a'), ('b', 'b', 'bbbbbbbbbb'), ('c', 'c', 'c')]
    
    要查找每列所需的长度,可以使用
    max

    >>> trans_a = zip(*a)
    >>> [max(len(c) for c in b) for b in trans_a]
    [10, 10, 1]
    
    通过适当的填充,您可以使用它来构造要传递给
    打印的字符串:

    >>> col_lenghts = [max(len(c) for c in b) for b in trans_a]
    >>> padding = ' ' # You might want more
    >>> padding.join(s.ljust(l) for s,l in zip(a[0], col_lenghts))
    'a          b          c'
    

    自Python 2.6+以来,您可以通过以下方式使用a将列设置为至少20个字符,并将文本向右对齐

    table_data = [
        ['a', 'b', 'c'],
        ['aaaaaaaaaa', 'b', 'c'], 
        ['a', 'bbbbbbbbbb', 'c']
    ]
    for row in table_data:
        print("{: >20} {: >20} {: >20}".format(*row))
    
    输出:

                   a                    b                    c
          aaaaaaaaaa                    b                    c
                   a           bbbbbbbbbb                    c
    

    我是带着同样的要求来到这里的,但@lvc和@Preet的答案似乎更符合
    列-t
    产生的结果,因为列的宽度不同:

    >>> rows =  [   ['a',           'b',            'c',    'd']
    ...         ,   ['aaaaaaaaaa',  'b',            'c',    'd']
    ...         ,   ['a',           'bbbbbbbbbb',   'c',    'd']
    ...         ]
    ...
    


    为了得到更漂亮的桌子,比如

    ---------------------------------------------------
    | First Name | Last Name        | Age | Position  |
    ---------------------------------------------------
    | John       | Smith            | 24  | Software  |
    |            |                  |     | Engineer  |
    ---------------------------------------------------
    | Mary       | Brohowski        | 23  | Sales     |
    |            |                  |     | Manager   |
    ---------------------------------------------------
    | Aristidis  | Papageorgopoulos | 28  | Senior    |
    |            |                  |     | Reseacher |
    ---------------------------------------------------
    
    您可以使用以下选项:

    包含一些改进。

    基于创建数据帧的解决方案:

    import pandas as pd
    l = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
    df = pd.DataFrame(l)
    
    print(df)
                0           1  2
    0           a           b  c
    1  aaaaaaaaaa           b  c
    2           a  bbbbbbbbbb  c
    
                for index, row in clipped.iterrows():
                    rdName      = self.colorize(row['name1'],"\033[32m")
                    rdClass     = self.colorize(row['roadClassification'],"\033[93m")
                    rdFunction  = self.colorize(row['roadFunction'],"\033[33m")
                    rdForm      = self.colorize(row['formOfWay'],"\033[94m")
                    rdLength    = self.colorize(row['length'],"\033[97m")
                    rdDistance  = self.colorize(row['distance'],"\033[96m")
                    print('{0:<30} {1:>35} {2:>35} {3:>35} {4:>20} {5:>20}'.format(rdName,rdClass,rdFunction,rdForm,rdLength,rdDistance) )
    
    要删除索引和标题值以创建所需的输出,可以使用以下方法:


    我意识到这个问题很老,但我不理解Antak的答案,也不想使用库,所以我推出了自己的解决方案

    解决方案假设记录是二维数组,记录的长度都相同,字段都是字符串

    def stringifyRecords(records):
        column_widths = [0] * len(records[0])
        for record in records:
            for i, field in enumerate(record):
                width = len(field)
                if width > column_widths[i]: column_widths[i] = width
    
        s = ""
        for record in records:
            for column_width, field in zip(column_widths, record):
                s += field.ljust(column_width+1)
            s += "\n"
    
        return s
    

    我发现这个答案非常有帮助和优雅,最初来自:

    输出

    A   B
    C   D
    
    first row:                    a                    b                    c
    second row:           aaaaaaaaaa                    b                    c
    third row:                    a           bbbbbbbbbb                    c
    
    first row:                              a                    b                    c
    second row:                    aaaaaaaaaa                    b                    c
    third row:                     aaaaaaaaaa                    b                    c
    

    下面是肖恩·琴回答的一个变体。每列的宽度是固定的,而不是所有列的宽度。在第一行下方和列之间还有一个边框。(库用于强制执行合同。)

    以下是一个例子:

    >>> table = [['column 0', 'another column 1'], ['00', '01'], ['10', '11']]
    >>> result = packagery._format_table(table=table)
    >>> print(result)
    column 0 | another column 1
    ---------+-----------------
    00       | 01              
    10       | 11              
    

    这是一个有点晚的聚会,一个无耻的插件为一个包我写的,但你也可以检查包

    它接受输入列表和标题列表,并输出一个表格式的字符串。此代码段创建一个docker样式的表:

    从columnar导入columnar
    headers=['name','id','host','notes']
    数据=[
    ['busybox'、'c3c37d5d-38d2-409f-8d02-600fd9d51239'、'linuxnode-1-292735'、'测试服务器',
    ['alpine-python'、'6bb77855-0fda-45a9-b553-e19e1a795f1e'、'linuxnode-2-249253'、'运行python的那个',
    ['redis'、'afb648ba-ac97-4fb2-8953-9a5b5f39663e'、'linuxnode-3-3416918'、'用于队列和其他东西',
    [“应用程序服务器”、“b866cd0f-bf80-40c7-84e3-c40891ec68f9”、“linuxnode-4-295918”、“热门目的地”。],
    ['nginx','76fea0f0-aa53-4911-b7e4-fae28c2e469b','linuxnode-5-292735','Traffic Cop'],
    ]
    表=列(数据、标题、无边框=真)
    打印(表格)
    

    或者你可以找一个有颜色和边框的小爱好者。

    要阅读有关列大小调整算法的更多信息并查看API的其余部分,您可以查看上面的链接或查看

    是一个新的库,它允许您在自动调整列宽的同时轻松打印流式列数据


    (免责声明:我是作者)

    更新了@Franck Dernoncourt fancy配方,使其符合python 3和PEP8

    import io
    import math
    import operator
    import re
    import functools
    
    from itertools import zip_longest
    
    
    def indent(
        rows,
        has_header=False,
        header_char="-",
        delim=" | ",
        justify="left",
        separate_rows=False,
        prefix="",
        postfix="",
        wrapfunc=lambda x: x,
    ):
        """Indents a table by column.
           - rows: A sequence of sequences of items, one sequence per row.
           - hasHeader: True if the first row consists of the columns' names.
           - headerChar: Character to be used for the row separator line
             (if hasHeader==True or separateRows==True).
           - delim: The column delimiter.
           - justify: Determines how are data justified in their column.
             Valid values are 'left','right' and 'center'.
           - separateRows: True if rows are to be separated by a line
             of 'headerChar's.
           - prefix: A string prepended to each printed row.
           - postfix: A string appended to each printed row.
           - wrapfunc: A function f(text) for wrapping text; each element in
             the table is first wrapped by this function."""
    
        # closure for breaking logical rows to physical, using wrapfunc
        def row_wrapper(row):
            new_rows = [wrapfunc(item).split("\n") for item in row]
            return [[substr or "" for substr in item] for item in zip_longest(*new_rows)]
    
        # break each logical row into one or more physical ones
        logical_rows = [row_wrapper(row) for row in rows]
        # columns of physical rows
        columns = zip_longest(*functools.reduce(operator.add, logical_rows))
        # get the maximum of each column by the string length of its items
        max_widths = [max([len(str(item)) for item in column]) for column in columns]
        row_separator = header_char * (
            len(prefix) + len(postfix) + sum(max_widths) + len(delim) * (len(max_widths) - 1)
        )
        # select the appropriate justify method
        justify = {"center": str.center, "right": str.rjust, "left": str.ljust}[
            justify.lower()
        ]
        output = io.StringIO()
        if separate_rows:
            print(output, row_separator)
        for physicalRows in logical_rows:
            for row in physicalRows:
                print( output, prefix + delim.join(
                    [justify(str(item), width) for (item, width) in zip(row, max_widths)]
                ) + postfix)
            if separate_rows or has_header:
                print(output, row_separator)
                has_header = False
        return output.getvalue()
    
    
    # written by Mike Brown
    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
    def wrap_onspace(text, width):
        """
        A word-wrap function that preserves existing line breaks
        and most spaces in the text. Expects that existing line
        breaks are posix newlines (\n).
        """
        return functools.reduce(
            lambda line, word, i_width=width: "%s%s%s"
            % (
                line,
                " \n"[
                    (
                        len(line[line.rfind("\n") + 1 :]) + len(word.split("\n", 1)[0])
                        >= i_width
                    )
                ],
                word,
            ),
            text.split(" "),
        )
    
    
    def wrap_onspace_strict(text, i_width):
        """Similar to wrap_onspace, but enforces the width constraint:
           words longer than width are split."""
        word_regex = re.compile(r"\S{" + str(i_width) + r",}")
        return wrap_onspace(
            word_regex.sub(lambda m: wrap_always(m.group(), i_width), text), i_width
        )
    
    
    def wrap_always(text, width):
        """A simple word-wrap function that wraps text on exactly width characters.
           It doesn't split the text in words."""
        return "\n".join(
            [
                text[width * i : width * (i + 1)]
                for i in range(int(math.ceil(1.0 * len(text) / width)))
            ]
        )
    
    
    if __name__ == "__main__":
        labels = ("First Name", "Last Name", "Age", "Position")
        data = """John,Smith,24,Software Engineer
               Mary,Brohowski,23,Sales Manager
               Aristidis,Papageorgopoulos,28,Senior Reseacher"""
        rows = [row.strip().split(",") for row in data.splitlines()]
    
        print("Without wrapping function\n")
        print(indent([labels] + rows, has_header=True))
    
        # test indent with different wrapping functions
        width = 10
        for wrapper in (wrap_always, wrap_onspace, wrap_onspace_strict):
            print("Wrapping function: %s(x,width=%d)\n" % (wrapper.__name__, width))
    
            print(
                indent(
                    [labels] + rows,
                    has_header=True,
                    separate_rows=True,
                    prefix="| ",
                    postfix=" |",
                    wrapfunc=lambda x: wrapper(x, width),
                )
            )
    
        # output:
        #
        # Without wrapping function
        #
        # First Name | Last Name        | Age | Position
        # -------------------------------------------------------
        # John       | Smith            | 24  | Software Engineer
        # Mary       | Brohowski        | 23  | Sales Manager
        # Aristidis  | Papageorgopoulos | 28  | Senior Reseacher
        #
        # Wrapping function: wrap_always(x,width=10)
        #
        # ----------------------------------------------
        # | First Name | Last Name  | Age | Position   |
        # ----------------------------------------------
        # | John       | Smith      | 24  | Software E |
        # |            |            |     | ngineer    |
        # ----------------------------------------------
        # | Mary       | Brohowski  | 23  | Sales Mana |
        # |            |            |     | ger        |
        # ----------------------------------------------
        # | Aristidis  | Papageorgo | 28  | Senior Res |
        # |            | poulos     |     | eacher     |
        # ----------------------------------------------
        #
        # Wrapping function: wrap_onspace(x,width=10)
        #
        # ---------------------------------------------------
        # | First Name | Last Name        | Age | Position  |
        # ---------------------------------------------------
        # | John       | Smith            | 24  | Software  |
        # |            |                  |     | Engineer  |
        # ---------------------------------------------------
        # | Mary       | Brohowski        | 23  | Sales     |
        # |            |                  |     | Manager   |
        # ---------------------------------------------------
        # | Aristidis  | Papageorgopoulos | 28  | Senior    |
        # |            |                  |     | Reseacher |
        # ---------------------------------------------------
        #
        # Wrapping function: wrap_onspace_strict(x,width=10)
        #
        # ---------------------------------------------
        # | First Name | Last Name  | Age | Position  |
        # ---------------------------------------------
        # | John       | Smith      | 24  | Software  |
        # |            |            |     | Engineer  |
        # ---------------------------------------------
        # | Mary       | Brohowski  | 23  | Sales     |
        # |            |            |     | Manager   |
        # ---------------------------------------------
        # | Aristidis  | Papageorgo | 28  | Senior    |
        # |            | poulos     |     | Reseacher |
        # ---------------------------------------------
    

    这将根据其他答案中使用的最大度量设置独立的最佳列宽

    data = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
    padding = 2
    col_widths = [max(len(w) for w in [r[cn] for r in data]) + padding for cn in range(len(data[0]))]
    format_string = "{{:{}}}{{:{}}}{{:{}}}".format(*col_widths)
    for row in data:
        print(format_string.format(*row))
    
    懒惰的人 正在使用Python 3.*和熊猫/Geopandas的;通用简单类内方法(对于“普通”脚本,只需删除self):

    函数着色:

        def colorize(self,s,color):
            s = color+str(s)+"\033[0m"
            return s
    
    print('{0:<23} {1:>24} {2:>26} {3:>26} {4:>11} {5:>11}'.format('Road name','Classification','Function','Form of road','Length','Distance') )
    
    标题:

        def colorize(self,s,color):
            s = color+str(s)+"\033[0m"
            return s
    
    print('{0:<23} {1:>24} {2:>26} {3:>26} {4:>11} {5:>11}'.format('Road name','Classification','Function','Form of road','Length','Distance') )
    
    print({0:24}{2:>26}{3:>26}{4:>11}{5:>11})。格式('Road name','Classification','Function','Form of Road','Length','Distance'))
    
    然后是熊猫/Geopandas数据帧中的数据:

    import pandas as pd
    l = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
    df = pd.DataFrame(l)
    
    print(df)
                0           1  2
    0           a           b  c
    1  aaaaaaaaaa           b  c
    2           a  bbbbbbbbbb  c
    
                for index, row in clipped.iterrows():
                    rdName      = self.colorize(row['name1'],"\033[32m")
                    rdClass     = self.colorize(row['roadClassification'],"\033[93m")
                    rdFunction  = self.colorize(row['roadFunction'],"\033[33m")
                    rdForm      = self.colorize(row['formOfWay'],"\033[94m")
                    rdLength    = self.colorize(row['length'],"\033[97m")
                    rdDistance  = self.colorize(row['distance'],"\033[96m")
                    print('{0:<30} {1:>35} {2:>35} {3:>35} {4:>20} {5:>20}'.format(rdName,rdClass,rdFunction,rdForm,rdLength,rdDistance) )
    
    对于clipped.iterrows()中的索引行:
    rName=self.colorize(行['name1'],“\033[32m”)
    rdClass=self.colorize(行['roadClassification'],“\033[93m”)
    rdFunction=self.colorize(行['roadFunction'],“\033[33m”)
    rdForm=self.colorize(行['formOfWay'],“\033[94m”)
    rdLength=self.colorize(行['length'],“\033[97m”)
    rdDistance=self.colorize(行['distance'],“\033[96m”)
    打印({0:35}{2:>35}{3:>35}{4:>20}{5:>20})。格式(rdName、rdClass、rdFunction、rdForm、rdLength、rdDistance))
    
    代码{0:35}{2:>35}{3:>35}{4:>20}{5:>20}

        def colorize(self,s,color):
            s = color+str(s)+"\033[0m"
            return s
    
    print('{0:<23} {1:>24} {2:>26} {3:>26} {4:>11} {5:>11}'.format('Road name','Classification','Function','Form of road','Length','Distance') )
    
    0、1、2、3、4、5列
    ->本例共有6列

    30,35,20
    ->列的宽度(请注意,您必须添加长度
    \033[96m
    -这对于Python也是一个字符串),只需进行实验:)

    ,justify:right,left(也有
    =
    用于填充零)

    若你们想区分,比如最大值,你们必须切换到特殊的熊猫风格的函数,但假设这足够在终端窗口上显示数据

    结果:

    与之前的答案略有不同(我没有足够的代表对此进行评论)。格式库允许您指定元素的宽度和对齐方式,但不能指定元素的起始位置,也就是说,您可以说“20列宽”,但不能说“从20列开始”。这导致了此问题:

    table_data = [
        ['a', 'b', 'c'],
        ['aaaaaaaaaa', 'b', 'c'], 
        ['a', 'bbbbbbbbbb', 'c']
    ]
    
    print("first row: {: >20} {: >20} {: >20}".format(*table_data[0]))
    print("second row: {: >20} {: >20} {: >20}".format(*table_data[1]))
    print("third row: {: >20} {: >20} {: >20}".format(*table_data[2]))
    
    输出

    A   B
    C   D
    
    first row:                    a                    b                    c
    second row:           aaaaaaaaaa                    b                    c
    third row:                    a           bbbbbbbbbb                    c
    
    first row:                              a                    b                    c
    second row:                    aaaaaaaaaa                    b                    c
    third row:                     aaaaaaaaaa                    b                    c
    
    当然,答案是格式化文本字符串,这与格式结合起来有点奇怪:

    table_data = [
        ['a', 'b', 'c'],
        ['aaaaaaaaaa', 'b', 'c'], 
        ['a', 'bbbbbbbbbb', 'c']
    ]
    
    print(f"{'first row:': <20} {table_data[0][0]: >20} {table_data[0][1]: >20} {table_data[0][2]: >20}")
    print("{: <20} {: >20} {: >20} {: >20}".format(*['second row:', *table_data[1]]))
    print("{: <20} {: >20} {: >20} {: >20}".format(*['third row:', *table_data[1]]))
    

    您可以准备数据并将其传递给real
    实用程序

    让我们假设