สร้างเอาต์พุตคอลัมน์ที่ดีใน python


114

ฉันพยายามสร้างรายการคอลัมน์ที่ดีใน python เพื่อใช้กับเครื่องมือผู้ดูแลระบบ commandline ที่ฉันสร้าง

โดยพื้นฐานแล้วฉันต้องการรายการเช่น:

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

ในการเปลี่ยนเป็น:

a            b            c
aaaaaaaaaa   b            c
a            bbbbbbbbbb   c

การใช้แท็บธรรมดาจะไม่ทำเคล็ดลับที่นี่เพราะฉันไม่รู้ข้อมูลที่ยาวที่สุดในแต่ละแถว

นี่เป็นลักษณะการทำงานเดียวกับ 'column -t' ใน 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

ฉันมองไปรอบ ๆ เพื่อหาไลบรารี python ต่างๆเพื่อทำสิ่งนี้ แต่ไม่พบสิ่งที่เป็นประโยชน์


4
การใช้ ncurses นั้นเกินความจำเป็นเล็กน้อยสำหรับการแสดงข้อมูลขนาดเล็ก ~ 10 บรรทัดที่ฉันต้องการ .. แต่เราใช้ ncurses สำหรับสิ่งอื่น ๆ
xeor

คำตอบ:


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   

สิ่งนี้จะคำนวณการป้อนข้อมูลที่ยาวที่สุดเพื่อกำหนดความกว้างของคอลัมน์จากนั้นใช้.ljust()เพื่อเพิ่มช่องว่างภายในที่จำเป็นเมื่อพิมพ์แต่ละคอลัมน์


1
ชื่อlongestนี้ทำให้เข้าใจผิดไม่ใช่องค์ประกอบที่ยาวที่สุด แต่เป็น max_length BTW ที่ยาวที่สุดสามารถทำได้ด้วยสิ่งที่ชอบ: max((w for sub in data for w in sub), key=len). [ป.ล. ฉันไม่ใช่คนที่จะโหวตให้คะแนน]
Rik Poggi

1
max((w for ...), key=len)ให้รายการที่ยาวที่สุดแก่คุณจากนั้นคุณจะต้องวิ่งlenอีกครั้ง ไม่สามารถตัดสินใจได้ว่าสิ่งใดชัดเจนดังนั้นฉันจึงติดอยู่กับข้อแรก จุดดีเกี่ยวกับชื่อ var ที่ทำให้เข้าใจผิด การเปลี่ยนแปลง
Shawn Chin

1
ใช่ไม่มีความแตกต่างอย่างมากกับอย่างใดอย่างหนึ่งเพียงแค่เรื่องของรสนิยมที่ฉันพูด นอกเหนือจากนั้นอย่างที่คุณสังเกตเห็นว่าบรรทัดนั้นค่อนข้างสับสน (เกินไป) มันจะดีกว่าถ้าทำโดยตรง: max(len(x) for sub in data for x in sub)โดยที่ไม่สร้างรายการที่ไม่จำเป็นด้วย
Rik Poggi

1
ขอบคุณ! นี่คือสิ่งที่ฉันต้องการ อย่างไรก็ตามฉันต้องทำให้มันทำงานร่วมกับ python 2.4 ได้เช่นกันดังนั้นฉันจึงต่อ chain.from_iterable และแทนที่ col_width ด้วย max (len (x) สำหรับ sub ในข้อมูลสำหรับ x in sub) + 2 ตามที่แนะนำ หวังว่าคุณจะสามารถเปลี่ยนรหัสของคุณด้านบนเพื่อให้ชัดเจนหากมีคนอื่นต้องการให้รันด้วย 2.4
xeor

1
สิ่งนี้จะทำให้คอลัมน์ทั้งหมดมีความกว้างเท่ากันซึ่งไม่ใช่สิ่งที่column -tทำ
ปรีชา

145

ตั้งแต่ Python 2.6+ คุณสามารถใช้สตริงรูปแบบด้วยวิธีต่อไปนี้เพื่อตั้งค่าคอลัมน์ให้มีอักขระอย่างน้อย 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

10
ทางออกที่ดีที่สุด ณ ตอนนี้
zlr

สิ่งนี้เคยแสดงเพียง 9 รายการเมื่อฉันพยายามใช้
Dorian Dore

และคุณสามารถเพิ่มได้{: >20}เรื่อย ๆ เพื่อแสดงฟิลด์เพิ่มเติม
PercussiveRepair

8
การเพิ่มลงในโซลูชันจาก KurzedMetal ในตัวระบุรูปแบบที่แสดงด้านบน {:> 20}> แสดงการจัดตำแหน่งที่ถูกต้อง เมื่อใช้ {: <20} คุณจะได้คอลัมน์ที่จัดชิดซ้ายและเมื่อใช้ {: ^ 20} คุณจะได้คอลัมน์ที่จัดชิดกึ่งกลาง
Dale Moore

1
ฉันไม่คิดว่านี่จะตอบคำถาม - ดูเหมือนว่า OP ต้องการทำให้แต่ละแถวไม่กว้างเกินกว่าที่จะต้องเก็บเนื้อหาไว้ เพียงแค่ตั้งค่าความกว้างคงที่เป็น 20
สัญชาตญาณ

43

ฉันมาที่นี่ด้วยข้อกำหนดเดียวกัน แต่คำตอบของ @lvc และ @ Preet ดูเหมือนจะสอดคล้องกับสิ่งที่column -tสร้างในคอลัมน์นั้นมีความกว้างต่างกัน:

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

2
ดี นี่คือทางออกที่ชัดเจนที่สุดที่เป็นไปตาม "สเป็ค" ดั้งเดิมจริงๆ
ปรีชา

2
นี่คือวิธีแก้ปัญหาที่ได้ผลสำหรับฉัน โซลูชันอื่น ๆ ให้เอาต์พุตคอลัมน์ แต่วิธีนี้ให้การควบคุมช่องว่างภายในมากที่สุดพร้อมกับความกว้างของคอลัมน์ที่ถูกต้อง
Michael J

1
ทางออกที่สวยงาม สำหรับคอลัมน์ใด ๆ map(len, map(str, col))ที่ไม่สตริงเพียงเพิ่มแผนที่พิเศษ:
Druckles

11

นี่เป็นงานปาร์ตี้สายเล็กน้อยและเป็นปลั๊กไร้ยางอายสำหรับแพ็คเกจที่ฉันเขียน แต่คุณสามารถตรวจสอบแพ็คเกจColumnarได้

ใช้ลิสต์รายการอินพุตและรายการส่วนหัวและเอาต์พุตสตริงที่จัดรูปแบบตาราง ตัวอย่างนี้สร้างตารางนักเทียบท่า:

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)

ตารางแสดงรูปแบบไม่มีขอบ

หรือคุณอาจจะดูน่าสนใจขึ้นด้วยสีและเส้นขอบ ตารางแสดงสปริงคลาสสิก

หากต้องการอ่านเพิ่มเติมเกี่ยวกับอัลกอริทึมการปรับขนาดคอลัมน์และดูส่วนที่เหลือของ API คุณสามารถดูลิงก์ด้านบนหรือดูColumnar GitHub Repo


แพ็คเกจนี้ใช้งานได้ดีขอบคุณสำหรับการแบ่งปัน
เคยชิน

8

คุณต้องทำสิ่งนี้ด้วยบัตร 2 ใบ:

  1. รับความกว้างสูงสุดของแต่ละคอลัมน์
  2. การจัดรูปแบบคอลัมน์โดยใช้ความรู้ของเราเกี่ยวกับความกว้างสูงสุดจากการส่งครั้งแรกโดยใช้str.ljust()และstr.rjust()

7

การย้ายคอลัมน์เช่นนี้เป็นงานสำหรับ 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]

ซึ่งคุณสามารถใช้โดยมีช่องว่างภายในที่เหมาะสมเพื่อสร้างสตริงที่จะส่งผ่านไปยัง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'

6

เพื่อให้ได้ตารางที่น่าสนใจยิ่งขึ้นเช่น

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

คุณสามารถใช้สูตร Python นี้ :

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

หน้าสูตรหลามมีการปรับปรุงไม่กี่กับมัน


5

pandas โซลูชันที่ใช้กับการสร้าง dataframe:

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

ในการลบดัชนีและค่าส่วนหัวเพื่อสร้างผลลัพธ์ที่คุณต้องการคุณสามารถใช้to_stringวิธีการ:

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

print(result)
          a           b  c
 aaaaaaaaaa           b  c
          a  bbbbbbbbbb  c

1

Scolpเป็นไลบรารีใหม่ที่ให้คุณพิมพ์ข้อมูลคอลัมน์แบบสตรีมมิ่งได้อย่างง่ายดายในขณะที่ปรับความกว้างของคอลัมน์อัตโนมัติ

(ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน)


1

ค่านี้จะตั้งค่าความกว้างของคอลัมน์ที่เป็นอิสระและเหมาะสมที่สุดตามเมตริกสูงสุดที่ใช้ในคำตอบอื่น ๆ

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

นี่คือคำตอบที่เหนือกว่าแม้ว่ารหัสจะมีกลิ่นเหม็นเล็กน้อย
Sam Morgan

1

สำหรับคนขี้เกียจ

ที่ใช้Python 3. *และPandas / Geopandas ; แนวทางสากลที่เรียบง่ายในชั้นเรียน (สำหรับสคริปต์ 'ปกติ' เพียงแค่ลบตัวเองออก):

ฟังก์ชัน colorize:

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

จากนั้นข้อมูลจาก Pandas / Geopandas dataframe:

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

ความหมายของ{0:<30} {1:>35} {2:>35} {3:>35} {4:>20} {5:>20}:

0, 1, 2, 3, 4, 5 -> คอลัมน์ในกรณีนี้มีทั้งหมด 6 คอลัมน์

30, 35, 20-> ความกว้างของคอลัมน์ (โปรดทราบว่าคุณจะต้องเพิ่มความยาว\033[96m- สิ่งนี้สำหรับ Python เป็นสตริงเช่นกัน) เพียงทดลอง :)

>, <-> justify: ขวา, ซ้าย (มี=สำหรับเติมเลขศูนย์ด้วย)

หากคุณต้องการแตกต่างเช่นค่าสูงสุดคุณจะต้องเปลี่ยนไปใช้ฟังก์ชันพิเศษสไตล์ Pandas แต่สมมติว่ามีข้อมูลอยู่ในหน้าต่างเทอร์มินัลมากพอ

ผลลัพธ์:

ใส่คำอธิบายภาพที่นี่


1

การเปลี่ยนแปลงเล็กน้อยในคำตอบก่อนหน้านี้ (ฉันมีตัวแทนไม่เพียงพอที่จะแสดงความคิดเห็น) ไลบรารีรูปแบบช่วยให้คุณสามารถระบุความกว้างและการจัดแนวขององค์ประกอบ แต่ไม่สามารถระบุจุดเริ่มต้นขององค์ประกอบกล่าวคือคุณสามารถพูดว่า "กว้าง 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]))

เอาท์พุต

first row:                    a                    b                    c
second row:           aaaaaaaaaa                    b                    c
third row:                    a           bbbbbbbbbb                    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]]))

เอาท์พุต

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


0

นี่คือรูปแบบของคำตอบของ Shawn Chin ความกว้างคงที่ต่อคอลัมน์ไม่เกินคอลัมน์ทั้งหมด นอกจากนี้ยังมีเส้นขอบด้านล่างแถวแรกและระหว่างคอลัมน์ ( ไลบรารีicontractใช้เพื่อบังคับใช้สัญญา)

@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

นี่คือตัวอย่าง:

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

0

อัปเดตสูตรแฟนซีของ @Franck Dernoncourt ให้เป็นไปตามหลาม 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 |
    # ---------------------------------------------

-1

ฉันรู้ว่าคำถามนี้เก่า แต่ฉันไม่เข้าใจคำตอบของ Antak และไม่ต้องการใช้ห้องสมุดดังนั้นฉันจึงรวบรวมวิธีแก้ปัญหาของตัวเอง

โซลูชันถือว่าเร็กคอร์ดเป็นอาร์เรย์ 2 มิติเร็กคอร์ดมีความยาวเท่ากันทั้งหมดและฟิลด์นั้นเป็นสตริงทั้งหมด

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.