Buat keluaran kolom yang bagus dengan python

113

Saya mencoba membuat daftar kolom yang bagus dengan python untuk digunakan dengan alat admin baris perintah yang saya buat.

Pada dasarnya, saya ingin daftar seperti:

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

Untuk berubah menjadi:

a            b            c
aaaaaaaaaa   b            c
a            bbbbbbbbbb   c

Menggunakan tab biasa tidak akan berhasil di sini karena saya tidak tahu data terpanjang di setiap baris.

Ini adalah perilaku yang sama dengan 'kolom -t' di 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

Saya telah mencari-cari berbagai pustaka python untuk melakukan ini tetapi tidak dapat menemukan sesuatu yang berguna.

xeor
sumber
4
Menggunakan ncurses agak berlebihan untuk menampilkan ~ 10 baris informasi yang saya inginkan .. Tapi kami menggunakan ncurses untuk hal-hal lain.
xeor

Jawaban:

121
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   

Apa yang dilakukannya adalah menghitung entri data terpanjang untuk menentukan lebar kolom, lalu gunakan .ljust()untuk menambahkan bantalan yang diperlukan saat mencetak setiap kolom.

Shawn Chin
sumber
1
Namanya longestmenyesatkan karena ini bukan elemen terpanjang tapi max_length. BTW terpanjang bisa diambil dengan sesuatu seperti: max((w for sub in data for w in sub), key=len). [PS Saya bukan orang yang tidak suka]
Rik Poggi
1
max((w for ...), key=len)memberi Anda entri terpanjang dan Anda harus berlari lenlagi. Tidak bisa memutuskan mana yang jelas jadi saya tetap dengan yang pertama. Poin bagus untuk nama var yang menyesatkan. Berubah.
Shawn Chin
1
Ya, tidak ada perbedaan besar satu sama lain, hanya soal selera saja yang akan saya katakan. Selain itu, seperti yang Anda perhatikan, baris itu (terlalu) membingungkan. Akan lebih baik melakukannya secara langsung:, max(len(x) for sub in data for x in sub)itu juga tidak membangun daftar yang tidak perlu.
Rik Poggi
1
Terima kasih! Inilah yang saya butuhkan. Namun, saya harus membuatnya berfungsi dengan python 2.4 juga, jadi saya menghapus chain.from_iterable, dan mengganti col_width dengan max (len (x) untuk sub dalam data untuk x di sub) + 2 seperti yang disarankan. Semoga Anda dapat mengubah kode Anda di atas untuk memperjelas jika orang lain ingin ini berjalan dengan 2.4.
xeor
1
Ini akan membuat semua kolom memiliki lebar yang sama, bukan itu yang column -tdilakukannya.
Intuisi
145

Sejak Python 2.6+, Anda dapat menggunakan string format dengan cara berikut untuk mengatur kolom menjadi minimal 20 karakter dan meratakan teks ke kanan.

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

Keluaran:

               a                    b                    c
      aaaaaaaaaa                    b                    c
               a           bbbbbbbbbb                    c
KurzedMetal
sumber
10
sejauh ini solusi terbaik saat ini
zlr
Ini hanya menampilkan 9 item ketika saya mencoba menggunakannya.
Dorian Dore
Dan Anda bisa terus menambahkan {: >20}untuk menampilkan lebih banyak bidang
PercussiveRepair
8
Menambahkan ke solusi dari KurzedMetal, dalam penentu format yang ditunjukkan di atas; {:> 20},> menunjukkan perataan kanan. Dengan menggunakan {: <20} Anda akan mendapatkan kolom rata kiri dan dengan menggunakan {: ^ 20} Anda akan mendapatkan kolom rata tengah.
Dale Moore
1
Saya rasa ini tidak menjawab pertanyaan itu — sepertinya OP ingin membuat setiap baris tidak lebih lebar dari yang seharusnya untuk menampung isinya. Ini hanya menetapkan lebar tetap 20.
dirilis
43

Saya datang ke sini dengan persyaratan yang sama tetapi jawaban @lvc dan @ Preet tampaknya lebih sejalan dengan apa yang column -tdihasilkan di kolom yang memiliki lebar berbeda:

>>> 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
sumber
2
Bagus. Ini adalah solusi paling jelas yang sebenarnya mengikuti "spesifikasi" asli.
Intuisi
2
Ini adalah solusi yang berhasil untuk saya. Solusi lain menghasilkan keluaran kolom tetapi yang satu ini memberikan kontrol paling besar pada bantalan bersama dengan lebar kolom yang akurat.
Michael J
1
Solusi yang bagus. Untuk setiap kolom bukan string, hanya menambahkan peta tambahan: map(len, map(str, col)).
Druckles
11

Ini agak terlambat ke pesta, dan steker tak tahu malu untuk paket yang saya tulis, tetapi Anda juga dapat memeriksa paket Columnar .

Dibutuhkan daftar daftar input dan daftar header dan output string berformat tabel. Cuplikan ini membuat tabel docker-esque:

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)

Tabel Menampilkan Gaya Tanpa Batas

Atau Anda bisa menjadi lebih mewah dengan warna dan tepian. Tabel Menampilkan Klasik Musim Semi

Untuk membaca lebih lanjut tentang algoritme ukuran kolom dan melihat API lainnya, Anda dapat melihat tautan di atas atau melihat Repo GitHub Kolom

Max Taggart
sumber
paket ini berfungsi dengan baik, terima kasih telah berbagi
pangsit
8

Anda harus melakukan ini dengan 2 operan:

  1. dapatkan lebar maksimum setiap kolom.
  2. memformat kolom menggunakan pengetahuan kita tentang lebar maks dari lintasan pertama menggunakan str.ljust()danstr.rjust()
Preet Kukreti
sumber
7

Mengubah urutan kolom seperti itu adalah pekerjaan untuk zip:

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

Untuk menemukan panjang yang dibutuhkan setiap kolom, Anda dapat menggunakan max:

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

Yang dapat Anda gunakan, dengan padding yang sesuai, untuk membuat string untuk diteruskan ke 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
sumber
6

Untuk mendapatkan tabel yang lebih bagus seperti

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

Anda dapat menggunakan resep Python ini :

'''
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 |
    #---------------------------------------------

The halaman Resep Python berisi beberapa perbaikan di atasnya.

Franck Dernoncourt
sumber
5

pandas solusi berbasis dengan membuat kerangka data:

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

Untuk menghapus nilai indeks dan header untuk membuat keluaran yang Anda inginkan, Anda dapat menggunakan to_stringmetode:

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

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

Scolp adalah pustaka baru yang memungkinkan Anda mencetak data kolom streaming dengan mudah sambil menyesuaikan lebar kolom secara otomatis.

(Penafian: Saya adalah penulisnya)

dux2.dll
sumber
1

Ini menetapkan lebar kolom independen dan paling pas berdasarkan metrik maks yang digunakan dalam jawaban lain.

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))
pengguna1529756
sumber
Ini adalah jawaban yang lebih baik, meskipun kodenya agak bau.
Sam Morgan
1

Untuk orang malas

yang menggunakan Python 3. * dan Pandas / Geopandas ; pendekatan kelas sederhana universal (untuk skrip 'normal' hapus saja sendiri ):

Fungsi mewarnai:

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

lalu data dari bingkai data Pandas / Geopandas:

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

Arti dari {0:<30} {1:>35} {2:>35} {3:>35} {4:>20} {5:>20}:

0, 1, 2, 3, 4, 5 -> kolom, ada total 6 dalam kasus ini

30, 35, 20-> lebar kolom (perhatikan bahwa Anda harus menambahkan panjang \033[96m- ini untuk Python adalah string juga), coba saja :)

>, <-> ratakan: kanan, kiri (ada juga yang =harus diisi dengan angka nol)

Jika Anda ingin membedakan misalnya nilai maks, Anda harus beralih ke fungsi gaya Pandas khusus, tetapi anggaplah itu cukup jauh untuk menampilkan data pada jendela terminal.

Hasil:

masukkan deskripsi gambar di sini

Vilq
sumber
1

Sedikit variasi pada jawaban sebelumnya (saya tidak memiliki cukup perwakilan untuk mengomentarinya). Pustaka format memungkinkan Anda menentukan lebar dan perataan elemen tetapi tidak di mana ia dimulai, yaitu, Anda dapat mengatakan "menjadi 20 kolom lebar" tetapi tidak "mulai di kolom 20". Yang mengarah ke masalah ini:

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

Keluaran

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

Jawabannya tentu saja adalah memformat string literal juga, yang menggabungkan sedikit aneh dengan format:

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

Keluaran

first row:                              a                    b                    c
second row:                    aaaaaaaaaa                    b                    c
third row:                     aaaaaaaaaa                    b                    c
aluchko
sumber
0

Saya menemukan jawaban ini sangat membantu dan elegan, awalnya dari sini :

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

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

Keluaran

A   B
C   D
Leonard
sumber
0

Berikut adalah variasi dari jawaban Shawn Chin. Lebar ditetapkan per kolom, tidak di semua kolom. Ada juga batas di bawah baris pertama dan di antara kolom. ( pustaka icontract digunakan untuk memberlakukan kontrak.)

@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

Berikut ini contohnya:

>>> 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
sumber
0

memperbarui resep mewah @Franck Dernoncourt agar sesuai dengan python 3 dan 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 |
    # ---------------------------------------------
pengguna2775234
sumber
-1

Saya menyadari pertanyaan ini sudah lama tetapi saya tidak mengerti jawaban Antak dan tidak ingin menggunakan perpustakaan jadi saya meluncurkan solusi saya sendiri.

Solusi mengasumsikan catatan adalah larik 2D, catatan memiliki panjang yang sama, dan semua bidang adalah string.

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
sumber