Python - 将字典作为带有表头的水平表格打印

46

我有一个字典:

import math
import random

d = {1: ["Spices", math.floor(random.gauss(40, 5))],
    2: ["Other stuff", math.floor(random.gauss(20, 5))],
    3: ["Tea", math.floor(random.gauss(50, 5))],
    10: ["Contraband", math.floor(random.gauss(1000, 5))],
    5: ["Fruit", math.floor(random.gauss(10, 5))],
    6: ["Textiles", math.floor(random.gauss(40, 5))]
}

我想打印它,以便它能与标题对齐。我可以把标题添加到字典中,始终确保它们排在最前面吗?
我见过一些垂直方式来做到这一点,但我希望它的列宽接近于最大的 str() 或 int()。

例如:

键___________________标签______________________数字
1______________________香料_____________________42
2______________________其他材料_____________16

显然,即使是手动在编辑器中进行此操作也不行,但我希望您明白我的意思。
我也不真的想要 __。 只需一个占位符。
谢谢大家。


5
你尝试过什么吗?我想到了这个:for key in d: print('{}\t{}\t{}'.format(key, d[key][0], d[key][1]))(在大括号内添加 :<width> 就可以自由调整宽度了)... - Henry Keiter
我不想使用选项卡,因为列宽度的可变性。虽然我尝试过这种方法。 - Faller
7个回答

84

您可以在Python2中使用字符串格式化

    print "{:<8} {:<15} {:<10}".format('Key','Label','Number')
    for k, v in d.iteritems():
        label, num = v
        print "{:<8} {:<15} {:<10}".format(k, label, num)

或者,Python3的字符串格式化

    print("{:<8} {:<15} {:<10}".format('Key','Label','Number'))
    for k, v in d.items():
        label, num = v
        print("{:<8} {:<15} {:<10}".format(k, label, num))

输出:

Key      Label           Number    
1        Spices          38.0      
2        Other stuff     24.0      
3        Tea             44.0      
5        Fruit           5.0       
6        Textiles        37.0      
10       Contraband      1000.0 

1
哦,我没意识到。谢谢。这比我预期的要容易。 - Faller
我有一个问题?如何使用Python将相同的格式添加到文件中。 - Ban
4
对于 Python 2.73.x 版本,使用 d.items() 替代 d.iteritems() - Giorgos Myrianthous

44

我正在寻找一种解决方案,可以打印数据库表格中未知列宽的内容。以下是解决方案:

def printTable(myDict, colList=None):
   """ Pretty print a list of dictionaries (myDict) as a dynamically sized table.
   If column names (colList) aren't specified, they will show in random order.
   Author: Thierry Husson - Use it as you want but don't blame me.
   """
   if not colList: colList = list(myDict[0].keys() if myDict else [])
   myList = [colList] # 1st row = header
   for item in myDict: myList.append([str(item[col] if item[col] is not None else '') for col in colList])
   colSize = [max(map(len,col)) for col in zip(*myList)]
   formatStr = ' | '.join(["{{:<{}}}".format(i) for i in colSize])
   myList.insert(1, ['-' * i for i in colSize]) # Seperating line
   for item in myList: print(formatStr.format(*item))

示例:

printTable([{'a':123,'bigtitle':456,'c':789},{'a':'x','bigtitle':'y','c':'z'}, \
    {'a':'2016-11-02','bigtitle':1.2,'c':78912313213123}], ['a','bigtitle','c'])

输出:

a          | bigtitle | c             
---------- | -------- | --------------
123        | 456      | 789           
x          | y        | z             
2016-11-02 | 1.2      | 78912313213123

在 Psycopg 上下文中,您可以按照以下方式使用它:
curPG.execute("SELECT field1, field2, ... fieldx FROM mytable")
printTable(curPG.fetchall(), [c.name for c in curPG.description])

如果您需要多行行的变体,这里提供一个解决方案:
def printTable(myDict, colList=None, sep='\uFFFA'):
   """ Pretty print a list of dictionaries (myDict) as a dynamically sized table.
   If column names (colList) aren't specified, they will show in random order.
   sep: row separator. Ex: sep='\n' on Linux. Default: dummy to not split line.
   Author: Thierry Husson - Use it as you want but don't blame me.
   """
   if not colList: colList = list(myDict[0].keys() if myDict else [])
   myList = [colList] # 1st row = header
   for item in myDict: myList.append([str(item[col] or '') for col in colList])
   colSize = [max(map(len,(sep.join(col)).split(sep))) for col in zip(*myList)]
   formatStr = ' | '.join(["{{:<{}}}".format(i) for i in colSize])
   line = formatStr.replace(' | ','-+-').format(*['-' * i for i in colSize])
   item=myList.pop(0); lineDone=False
   while myList or any(item):
      if all(not i for i in item):
         item=myList.pop(0)
         if line and (sep!='\uFFFA' or not lineDone): print(line); lineDone=True
      row = [i.split(sep,1) for i in item]
      print(formatStr.format(*[i[0] for i in row]))
      item = [i[1] if len(i)>1 else '' for i in row]

示例:

sampleDict = [{'multi lines title': 12, 'bigtitle': 456, 'third column': '7 8 9'},
{'multi lines title': 'w x y z', 'bigtitle': 'b1 b2', 'third column': 'z y x'},
{'multi lines title': '2', 'bigtitle': 1.2, 'third column': 78912313213123}]

printTable(sampleDict, sep=' ')

输出:

bigtitle | multi | third         
         | lines | column        
         | title |               
---------+-------+---------------
456      | 12    | 7             
         |       | 8             
         |       | 9             
---------+-------+---------------
b1       | w     | z             
b2       | x     | y             
         | y     | x             
         | z     |               
---------+-------+---------------
1.2      | 2     | 78912313213123

没有使用sep参数,printTable(sampleDict)会给你输出以下结果:
bigtitle | multi lines title | third column  
---------+-------------------+---------------
456      | 12                | 7 8 9         
b1 b2    | w x y z           | z y x         
1.2      | 2                 | 78912313213123

美妙的函数。 - lilster
不错的函数!我的数据包含换行符,因为它有多行数据。假设 bigtitle 列的数据包含换行符,并且我想在该列空间内打印这些字符。这可能吗? - Karthi1234
1
@Karthi1234 不到 10 行代码不行,但在末尾这里(更新)给出了。 - Le Droid
喜欢它。谢谢! - Samuurai
如何将打印输出保存为表格格式的文件? - miu
2
我发现多行行表格中存在一个问题。当最后一行有多行时,它停止打印第一行。要修复它,只需在while循环中添加any(item)myList or any(item)(即while myList or any(item):)。 - Varun Govind

18

我更喜欢使用Pandas DataFrame

import pandas as pd
data = {'Name': ['a', 'b', 'c'], 'Age': [10, 11, 12]}
df = pd.DataFrame(data)
print(df)

输出:

  Name  Age
0    a   10
1    b   11
2    c   12

在这里查看有关漂亮地打印数据帧的更多信息here


如果字典中的所有值长度不相等怎么办? - AleB
尝试通过使用None填充来使字典中的所有值长度相等。 - vivek_reddy

5

字符串格式化提供了一个优雅简单的解决方案。本答案是对来自@Ashwini的好回答的Python 3更新。

str_fmt = "{:<8} {:<15} {:<10}"
print(str_fmt.format('Key','Label','Number'))
for k, v in d.items():
    label, num = v
    print(str_fmt.format(k, label, num))

3

基于Le Droid的代码,我为每一行添加了分隔符“-”,可以使打印更加清晰。感谢Le Droid。

def printTable(myDict, colList=None):
    if not colList: 
        colList = list(myDict[0].keys() if myDict else [])
    myList = [colList] # 1st row = header
    for item in myDict: 
        myList.append([str(item[col] or '') for col in colList])
    #maximun size of the col for each element
    colSize = [max(map(len,col)) for col in zip(*myList)]
    #insert seperating line before every line, and extra one for ending. 
    for i in  range(0, len(myList)+1)[::-1]:
         myList.insert(i, ['-' * i for i in colSize])
    #two format for each content line and each seperating line
    formatStr = ' | '.join(["{{:<{}}}".format(i) for i in colSize])
    formatSep = '-+-'.join(["{{:<{}}}".format(i) for i in colSize])
    for item in myList: 
        if item[0][0] == '-':
            print(formatSep.format(*item))
        else:
            print(formatStr.format(*item))

输出:

-----------+----------+---------------
a          | bigtitle | c             
-----------+----------+---------------
123        | 456      | 789           
-----------+----------+---------------
x          | y        | z             
-----------+----------+---------------
2016-11-02 | 1.2      | 78912313213123
-----------+----------+---------------

3

我总是使用pandas来整齐地打印表格类型的结构。

从您的数据结构创建一个pandas数据框架非常简单。

df = pd.DataFrame(
    [[key] + list(value) for key, value in d.items()], 
    columns=['Key', 'Label', 'Number']
)

columns 用于定义数据每列的名称。

之后,您可以将其打印出来(忽略 pandas 数据框架的索引),如下所示:

print(df.to_string(index=False))

你的输出将会打印为:
 Key       Label  Number
   1      Spices      40
   2 Other stuff      14
   3         Tea      52
  10  Contraband     992

是的。现在我甚至不使用to_string,因为大多数编辑器都能很好地呈现pandas输出。 - Faller
是的,你说得对。但我故意使用了 to_string 来不显示索引列。 - Suraj Regmi

-1
你可以使用 ljust 或 rjust 字符串方法:
print key.ljust(10), label.ljust(30), number.ljust(20)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接