Erstellen Sie eine schöne Spaltenausgabe in Python

112

Ich versuche, eine schöne Spaltenliste in Python für die Verwendung mit von mir erstellten Befehlszeilen-Admin-Tools zu erstellen.

Grundsätzlich möchte ich eine Liste wie:

[['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]

Zu etwas werden:

a            b            c
aaaaaaaaaa   b            c
a            bbbbbbbbbb   c

Die Verwendung einfacher Registerkarten reicht hier nicht aus, da ich nicht die längsten Daten in jeder Zeile kenne.

Dies ist das gleiche Verhalten wie 'column -t' unter Linux.

$ 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

Ich habe mich nach verschiedenen Python-Bibliotheken umgesehen, kann aber nichts Nützliches finden.

xeor
quelle
4
Die Verwendung von ncurses ist ein wenig übertrieben, um die kleinen ~ 10 Zeilen mit Informationen anzuzeigen, die ich möchte. Aber wir verwenden ncurses für andere Dinge.
xeor

Antworten:

120
data = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]

col_width = max(len(word) for row in data for word in row) + 2  # padding
for row in data:
    print "".join(word.ljust(col_width) for word in row)

a            b            c            
aaaaaaaaaa   b            c            
a            bbbbbbbbbb   c   

Dies berechnet die längste Dateneingabe, um die Spaltenbreite zu bestimmen, und .ljust()fügt dann beim Ausdrucken jeder Spalte die erforderliche Auffüllung hinzu.

Shawn Chin
quelle
1
Der Name longestist irreführend, weil er nicht das längste Element ist, sondern die maximale Länge. Übrigens könnte das längste mit so etwas wie genommen werden : max((w for sub in data for w in sub), key=len). [PS Ich war nicht derjenige, der abstimmte]
Rik Poggi
1
max((w for ...), key=len)gibt Ihnen den längsten Eintrag und Sie müssen dann lenerneut einen Lauf durchführen . Ich konnte mich nicht entscheiden, was klar war, also blieb ich beim ersten. Guter Punkt zum irreführenden Variablennamen. Geändert.
Shawn Chin
1
Ja, es gibt keinen großen Unterschied zum einen oder anderen, nur eine Geschmackssache, würde ich sagen. Abgesehen davon ist diese Zeile, wie Sie bemerkt haben, etwas (zu) verwirrt. Es wäre besser, es direkt zu machen : max(len(x) for sub in data for x in sub), das baut auch keine unnötigen Listen auf.
Rik Poggi
1
Vielen Dank! Genau das brauche ich. Ich musste es jedoch auch mit Python 2.4 zum Laufen bringen, also habe ich chain.from_iterable entfernt und col_width durch max (len (x) für sub in data für x in sub) + 2 ersetzt, wie vorgeschlagen. Ich hoffe, Sie können Ihren obigen Code ändern, um zu verdeutlichen, ob jemand anderes dies mit 2.4 ausführen möchte.
xeor
1
Dadurch werden alle Spalten gleich breit, was nicht der column -tFall ist.
Intuition
145

Da Python 2.6+ können Sie einen verwenden Format - String in der folgenden Art und Weise die Spalten auf ein Minimum von 20 Zeichen und align Text nach rechts zu setzen.

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

Ausgabe:

               a                    b                    c
      aaaaaaaaaa                    b                    c
               a           bbbbbbbbbb                    c
KurzedMetal
quelle
10
bei weitem die beste Lösung ab sofort
zlr
Dies zeigt immer nur 9 Elemente, als ich versuchte, es zu verwenden.
Dorian Dore
Und Sie können einfach weitere hinzufügen {: >20}, um weitere Felder anzuzeigen
PercussiveRepair
8
Hinzufügen zu der Lösung von KurzedMetal in dem oben gezeigten Formatbezeichner; {:> 20}, das> zeigt die richtige Ausrichtung an. Wenn Sie {: <20} verwenden, erhalten Sie eine linksbündige Spalte, und wenn Sie {: ^ 20} verwenden, erhalten Sie eine zentriert ausgerichtete Spalte.
Dale Moore
1
Ich glaube nicht, dass dies die Frage beantwortet - es klingt so, als ob das OP jede Zeile nicht breiter machen möchte, als es sein muss, um ihren Inhalt zu halten. Dies setzt nur eine feste Breite von 20.
intuitiv
42

Ich bin mit den gleichen Anforderungen hierher gekommen, aber die Antworten von @lvc und @ Preet scheinen eher mit dem übereinzustimmen column -t in diesen Spalten unterschiedliche Breiten haben:

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

>>> widths = [max(map(len, col)) for col in zip(*rows)]
>>> for row in rows:
...     print "  ".join((val.ljust(width) for val, width in zip(row, widths)))
...
a           b           c  d
aaaaaaaaaa  b           c  d
a           bbbbbbbbbb  c  d
Antak
quelle
2
Nett. Dies ist die klarste Lösung, die tatsächlich der ursprünglichen "Spezifikation" folgt.
Intuition
2
Dies ist die Lösung, die für mich funktioniert hat. Andere Lösungen erzeugten eine Säulenausgabe, aber diese gab die größte Kontrolle über die Polsterung zusammen mit genauen Säulenbreiten.
Michael J
1
Schöne Lösung. Fügen Sie für Spalten ohne Zeichenfolgen einfach eine zusätzliche Zuordnung hinzu : map(len, map(str, col)).
Druckles
10

Dies ist ein wenig spät für die Party und ein schamloser Plug für ein Paket, das ich geschrieben habe, aber Sie können auch die Columnar überprüfen Paket .

Es nimmt eine Liste von Eingabelisten und eine Liste von Headern und gibt eine tabellenformatierte Zeichenfolge aus. Dieses Snippet erstellt eine Docker-ähnliche Tabelle:

from columnar import columnar

headers = ['name', 'id', 'host', 'notes']

data = [
    ['busybox', 'c3c37d5d-38d2-409f-8d02-600fd9d51239', 'linuxnode-1-292735', 'Test server.'],
    ['alpine-python', '6bb77855-0fda-45a9-b553-e19e1a795f1e', 'linuxnode-2-249253', 'The one that runs python.'],
    ['redis', 'afb648ba-ac97-4fb2-8953-9a5b5f39663e', 'linuxnode-3-3416918', 'For queues and stuff.'],
    ['app-server', 'b866cd0f-bf80-40c7-84e3-c40891ec68f9', 'linuxnode-4-295918', 'A popular destination.'],
    ['nginx', '76fea0f0-aa53-4911-b7e4-fae28c2e469b', 'linuxnode-5-292735', 'Traffic Cop'],
]

table = columnar(data, headers, no_borders=True)
print(table)

Tabelle mit No-Border-Stil

Oder Sie können mit Farben und Rändern etwas schicker werden. Tabelle mit Spring Classics

Um mehr über den Spaltengrößenalgorithmus zu erfahren und den Rest der API zu sehen, können Sie den obigen Link oder das Columnar GitHub Repo lesen

Max Taggart
quelle
Dieses Paket funktioniert großartig, danke fürs Teilen
Wonton
8

Sie müssen dies mit 2 Durchgängen tun:

  1. Ermitteln Sie die maximale Breite jeder Spalte.
  2. Formatieren der Spalten mit unserem Wissen über die maximale Breite ab dem ersten Durchgang mit str.ljust()undstr.rjust()
Preet Kukreti
quelle
7

Das Transponieren der Spalten ist eine Aufgabe für zip:

>>> a = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
>>> list(zip(*a))
[('a', 'aaaaaaaaaa', 'a'), ('b', 'b', 'bbbbbbbbbb'), ('c', 'c', 'c')]

Um die erforderliche Länge jeder Spalte zu ermitteln, können Sie Folgendes verwenden max:

>>> trans_a = zip(*a)
>>> [max(len(c) for c in b) for b in trans_a]
[10, 10, 1]

Welche Sie mit geeigneter Auffüllung verwenden können, um Zeichenfolgen zu erstellen, an die übergeben werden soll print:

>>> 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'
lvc
quelle
6

Um schickere Tische wie zu bekommen

---------------------------------------------------
| First Name | Last Name        | Age | Position  |
---------------------------------------------------
| John       | Smith            | 24  | Software  |
|            |                  |     | Engineer  |
---------------------------------------------------
| Mary       | Brohowski        | 23  | Sales     |
|            |                  |     | Manager   |
---------------------------------------------------
| Aristidis  | Papageorgopoulos | 28  | Senior    |
|            |                  |     | Reseacher |
---------------------------------------------------

Sie können dieses Python-Rezept verwenden :

'''
From http://code.activestate.com/recipes/267662-table-indentation/
PSF License
'''
import cStringIO,operator

def indent(rows, hasHeader=False, headerChar='-', delim=' | ', justify='left',
           separateRows=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 rowWrapper(row):
        newRows = [wrapfunc(item).split('\n') for item in row]
        return [[substr or '' for substr in item] for item in map(None,*newRows)]
    # break each logical row into one or more physical ones
    logicalRows = [rowWrapper(row) for row in rows]
    # columns of physical rows
    columns = map(None,*reduce(operator.add,logicalRows))
    # get the maximum of each column by the string length of its items
    maxWidths = [max([len(str(item)) for item in column]) for column in columns]
    rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \
                                 len(delim)*(len(maxWidths)-1))
    # select the appropriate justify method
    justify = {'center':str.center, 'right':str.rjust, 'left':str.ljust}[justify.lower()]
    output=cStringIO.StringIO()
    if separateRows: print >> output, rowSeparator
    for physicalRows in logicalRows:
        for row in physicalRows:
            print >> output, \
                prefix \
                + delim.join([justify(str(item),width) for (item,width) in zip(row,maxWidths)]) \
                + postfix
        if separateRows or hasHeader: print >> output, rowSeparator; hasHeader=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 reduce(lambda line, word, width=width: '%s%s%s' %
                  (line,
                   ' \n'[(len(line[line.rfind('\n')+1:])
                         + len(word.split('\n',1)[0]
                              ) >= width)],
                   word),
                  text.split(' ')
                 )

import re
def wrap_onspace_strict(text, width):
    """Similar to wrap_onspace, but enforces the width constraint:
       words longer than width are split."""
    wordRegex = re.compile(r'\S{'+str(width)+r',}')
    return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width)

import math
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 xrange(int(math.ceil(1.*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, hasHeader=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, hasHeader=True, separateRows=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 |
    #---------------------------------------------

Die Python-Rezeptseite enthält einige Verbesserungen.

Franck Dernoncourt
quelle
5

pandas basierte Lösung zum Erstellen eines Datenrahmens:

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

Um Index- und Headerwerte zu entfernen und eine Ausgabe zu erstellen, die Sie möchten, können Sie folgende to_stringMethode verwenden:

result = df.to_string(index=False, header=False)

print(result)
          a           b  c
 aaaaaaaaaa           b  c
          a  bbbbbbbbbb  c
Anton Protopopov
quelle
1

Scolp ist eine neue Bibliothek, mit der Sie Streaming- einfache Weise drucken und gleichzeitig die Spaltenbreite automatisch anpassen können.

(Haftungsausschluss: Ich bin der Autor)

dux2
quelle
1

Dadurch werden unabhängige, am besten passende Spaltenbreiten basierend auf der in anderen Antworten verwendeten Maximalmetrik festgelegt.

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))
user1529756
quelle
Dies ist die überlegene Antwort, obwohl der Code ein bisschen stinkt.
Sam Morgan
1

Für faule Leute

die Python 3. * und Pandas / Geopandas verwenden ; universeller einfacher Ansatz in der Klasse (für 'normales' Skript entfernen Sie einfach self ):

Funktion kolorieren:

    def colorize(self,s,color):
        s = color+str(s)+"\033[0m"
        return s

Header:

print('{0:<23} {1:>24} {2:>26} {3:>26} {4:>11} {5:>11}'.format('Road name','Classification','Function','Form of road','Length','Distance') )

und dann Daten aus dem Pandas / Geopandas-Datenrahmen:

            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) )

Bedeutung von {0:<30} {1:>35} {2:>35} {3:>35} {4:>20} {5:>20}:

0, 1, 2, 3, 4, 5 -> Spalten, in diesem Fall gibt es insgesamt 6

30, 35, 20-> Breite der Spalte (beachten Sie, dass Sie die Länge hinzufügen müssen \033[96m- dies ist auch für Python eine Zeichenfolge), experimentieren Sie einfach :)

>, <-> begründen: rechts, links (gibt es auch =zum Füllen mit Nullen)

Wenn Sie z. B. den Maximalwert unterscheiden möchten, müssen Sie zu einer speziellen Pandas-Stilfunktion wechseln. Angenommen, dies ist weit genug, um Daten im Terminalfenster anzuzeigen.

Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Vilq
quelle
1

Eine leichte Abweichung von einer vorherigen Antwort (ich habe nicht genug Vertreter, um sie zu kommentieren). In der Formatbibliothek können Sie die Breite und Ausrichtung eines Elements angeben, jedoch nicht dort, wo es beginnt. Sie können also "20 Spalten breit sein", aber nicht "In Spalte 20 beginnen". Was zu diesem Problem führt:

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]))

Ausgabe

first row:                    a                    b                    c
second row:           aaaaaaaaaa                    b                    c
third row:                    a           bbbbbbbbbb                    c

Die Antwort ist natürlich, auch die Literalzeichenfolgen zu formatieren, was sich etwas seltsam mit dem Format verbindet:

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]]))

Ausgabe

first row:                              a                    b                    c
second row:                    aaaaaaaaaa                    b                    c
third row:                     aaaaaaaaaa                    b                    c
Aluchko
quelle
0

Ich fand diese Antwort super hilfreich und elegant, ursprünglich von hier :

matrix = [["A", "B"], ["C", "D"]]

print('\n'.join(['\t'.join([str(cell) for cell in row]) for row in matrix]))

Ausgabe

A   B
C   D
Leonard
quelle
0

Hier ist eine Variation der Antwort von Shawn Chin. Die Breite ist pro Spalte festgelegt, nicht über alle Spalten. Es gibt auch einen Rand unterhalb der ersten Zeile und zwischen den Spalten. (Die icontract- Bibliothek wird zur Durchsetzung der Verträge verwendet.)

@icontract.pre(
    lambda table: not table or all(len(row) == len(table[0]) for row in table))
@icontract.post(lambda table, result: result == "" if not table else True)
@icontract.post(lambda result: not result.endswith("\n"))
def format_table(table: List[List[str]]) -> str:
    """
    Format the table as equal-spaced columns.

    :param table: rows of cells
    :return: table as string
    """
    cols = len(table[0])

    col_widths = [max(len(row[i]) for row in table) for i in range(cols)]

    lines = []  # type: List[str]
    for i, row in enumerate(table):
        parts = []  # type: List[str]

        for cell, width in zip(row, col_widths):
            parts.append(cell.ljust(width))

        line = " | ".join(parts)
        lines.append(line)

        if i == 0:
            border = []  # type: List[str]

            for width in col_widths:
                border.append("-" * width)

            lines.append("-+-".join(border))

    result = "\n".join(lines)

    return result

Hier ist ein Beispiel:

>>> 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              
marko.ristin
quelle
0

@Franck Dernoncourt ausgefallenes Rezept aktualisiert, um Python 3 und PEP8 kompatibel zu sein

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 |
    # ---------------------------------------------
user2775234
quelle
-1

Mir ist klar, dass diese Frage alt ist, aber ich habe Antaks Antwort nicht verstanden und wollte keine Bibliothek verwenden, also habe ich meine eigene Lösung entwickelt.

Die Lösung geht davon aus, dass Datensätze ein 2D-Array sind, dass Datensätze alle dieselbe Länge haben und dass alle Felder Zeichenfolgen sind.

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
George Crowson
quelle