如何将字典列表打印为对齐 table?

How to print a list of dicts as an aligned table?

因此,在使用格式说明符解决了有关对齐的多个问题后,我仍然无法弄清楚为什么数字数据以波浪方式打印到标准输出。

def create_data(soup_object,max_entry=None):
    max_=max_entry
    entry=dict()
    for a in range(1,int(max_)+1):

        entry[a]={'Key':a,
        'Title':soup_object[a].div.text.strip(),
        'Link':soup_object[a].div.a['href'],
        'Seeds':soup_object[a](attrs={'align':'right'})[0].text.strip(),
        'Leechers':soup_object[a](attrs={'align':'right'})[1].text.strip()}

        yield entry[a]

tpb_get_data=tuple(create_data(soup_object=tpb_soup.body.table.find_all("tr"),max_entry=5))
for data in tpb_get_data:
    print('{0} {1:<11}  {2:<25} {3:<25} '.format(data['Key'], data['Title'], data['Seeds'],data['Leechers']))

我尝试将 f-strings 与格式说明符一起使用,但它仍然按以下方式打印数据,有人可以帮我解决这个问题吗?

 1 Salvation.S02E11.HDTV.x264-KILLERS  262         19 
 2 Salvation.S02E13.WEB.x264-TBS[ettv]  229         25 
 3 Salvation.S02E08.HDTV.x264-KILLERS  178         21 
 4 Salvation.S02E01.HDTV.x264-KILLERS  144          11 
 5 Salvation.S02E09.HDTV.x264-SVA[ettv]  129       14

我已经阅读了大部分关于此的问题,我想知道是否有原始方法而不是使用像 tabulate 这样的库,它做得很好。但我也想学习如何在没有任何库的情况下做到这一点。

你得到一个错位的结果,因为你没有计算正确的标题长度。您只保留了 11 个字符,第一个字符已经有 34 个字符了。

最简单的方法是让您的程序对您有用:

key_len,title_len,seed_len,leech_len = ( max(len(item[itemname]) for item in tpb_get_data) for itemname in ['Key','Title','Seeds','Leechers'] )

fmtstring = '{{:{:d}}} {{:{:d}}} {{:{:d}}} {{:{:d}}}'.format(key_len,title_len,seed_len,leech_len)

for data in tpb_get_data:
    print(fmtstring.format(data['Key'], data['Title'], data['Seeds'],data['Leechers']))

更好的结果

1 Salvation.S02E11.HDTV.x264-KILLERS   262 19
2 Salvation.S02E13.WEB.x264-TBS[ettv]  229 25
3 Salvation.S02E08.HDTV.x264-KILLERS   178 21
4 Salvation.S02E01.HDTV.x264-KILLERS   144 11
5 Salvation.S02E09.HDTV.x264-SVA[ettv] 129 14

(仅附加)

这是一种更通用的方法,它使用 to-print 键名称列表,并且能够即时生成所有其他所需的变量。它不需要对变量的名称进行硬编码,也不需要固定它们的顺序——顺序取自该列表。调整项目以显示全部在一个地方:同一个列表,get_items。可以在 fmtstring 行中更改输出分隔符,例如在项目之间使用制表符或更多空格。

get_items = ['Key','Title','Leechers','Seeds']
lengths = ( max(len(item[itemname]) for item in tpb_get_data) for itemname in get_items )
fmtstring = ' '.join(['{{:{:d}}}' for i in range(len(get_items))]).format(*lengths)

for data in tpb_get_data:
    print(fmtstring.format(*[data[key] for key in get_items]))

它的工作原理如下:

  1. lengths 列表填充了取自 get_items 列表的每个命名键的最大长度。
  2. 这个returns一个listfmtstring 为每个项目重复格式指令 {:d} 并填写数字。外部 {{:}}format 翻译成 {:} 所以最终结果将是 {:[=42=每个长度的 ]number</em>}。这些单独的格式字符串连接成一个较长的格式字符串。
  3. 最后,对实际数据的循环打印来自 get_items 的项目。列表理解查找它们; * 表示法强制列表 'written out' 作为单独的值,而不是将整个列表作为一个返回。

感谢 @Georgy 建议寻找一个不太硬编码的品种。

如前所述,您计算的字符串长度不正确。
不要对它们进行硬编码,而是将此任务委托给您的程序。

这是一个通用的方法:

from operator import itemgetter
from typing import (Any,
                    Dict,
                    Iterable,
                    Iterator,
                    List,
                    Sequence)


def max_length(objects: Iterable[Any]) -> int:
    """Returns maximum string length of a sequence of objects"""
    strings = map(str, objects)
    return max(map(len, strings))


def values_max_length(dicts: Sequence[Dict[str, Any]],
                      *,
                      key: str) -> int:
    """Returns maximum string length of dicts values for specific key"""
    return max_length(map(itemgetter(key), dicts))


def to_aligned_data(dicts: Sequence[Dict[str, Any]],
                    *,
                    keys: List[str],
                    sep: str = ' ') -> Iterator[str]:
    """Prints a sequence of dicts in a form of a left aligned table"""
    lengths = (values_max_length(dicts, key=key) 
               for key in keys)

    format_string = sep.join(map('{{:{}}}'.format, lengths))

    for row in map(itemgetter(*keys), dicts):
        yield format_string.format(*row)

示例:

data = [{'Key': '1',
         'Title': 'Salvation.S02E11.HDTV.x264-KILLERS',
         'Seeds': '262',
         'Leechers': '19'},
        {'Key': '2',
         'Title': 'Salvation.S02E13.WEB.x264-TBS[ettv]',
         'Seeds': '229',
         'Leechers': '25'},
        {'Key': '3',
         'Title': 'Salvation.S02E08.HDTV.x264-KILLERS',
         'Seeds': '178',
         'Leechers': '21'},
        {'Key': '4',
         'Title': 'Salvation.S02E01.HDTV.x264-KILLERS',
         'Seeds': '144',
         'Leechers': '11'},
        {'Key': '5',
         'Title': 'Salvation.S02E09.HDTV.x264-SVA[ettv]',
         'Seeds': '129',
         'Leechers': '14'}]
keys = ['Key', 'Title', 'Seeds', 'Leechers']
print(*to_aligned_data(data, keys=keys),
      sep='\n')
# 1 Salvation.S02E11.HDTV.x264-KILLERS   262 19
# 2 Salvation.S02E13.WEB.x264-TBS[ettv]  229 25
# 3 Salvation.S02E08.HDTV.x264-KILLERS   178 21
# 4 Salvation.S02E01.HDTV.x264-KILLERS   144 11
# 5 Salvation.S02E09.HDTV.x264-SVA[ettv] 129 14
keys = ['Title', 'Leechers']
print(*to_aligned_data(data, keys=keys),
      sep='\n')
# Salvation.S02E11.HDTV.x264-KILLERS   19
# Salvation.S02E13.WEB.x264-TBS[ettv]  25
# Salvation.S02E08.HDTV.x264-KILLERS   21
# Salvation.S02E01.HDTV.x264-KILLERS   11
# Salvation.S02E09.HDTV.x264-SVA[ettv] 14
keys = ['Key', 'Title', 'Seeds', 'Leechers']
print(*to_aligned_data(data, keys=keys, sep=' ' * 5),
      sep='\n')
# 1     Salvation.S02E11.HDTV.x264-KILLERS       262     19
# 2     Salvation.S02E13.WEB.x264-TBS[ettv]      229     25
# 3     Salvation.S02E08.HDTV.x264-KILLERS       178     21
# 4     Salvation.S02E01.HDTV.x264-KILLERS       144     11
# 5     Salvation.S02E09.HDTV.x264-SVA[ettv]     129     14

有关更多信息,请参阅 docs。还有对齐的例子。

很好的答案购买@Jongware,只是为了

  1. 让它更通用一些
  2. 没有硬编码项目
  3. 打印任何类型的值,而不仅仅是字符串 -

这里是:

def print_list_of_dicts_as_table(list_of_dicts, keys=None):
    # assuming all dicts have same keys
    first_entry = list_of_dicts[0]
    if keys is None:
        keys = first_entry.keys()
    num_keys = len(keys)

    max_key_lens = [
        max(len(str(item[k])) for item in list_of_dicts) for k in keys
    ]
    for k_idx, k in enumerate(keys):
        max_key_lens[k_idx] = max(max_key_lens[k_idx], len(k))

    fmtstring = (' | '.join(['{{:{:d}}}'] * num_keys)).format(*max_key_lens)

    print(fmtstring.format(*first_entry.keys()))
    print(fmtstring.format(*['-'*key_len for key_len in max_key_lens]))
    for entry in list_of_dicts:
        print(fmtstring.format(*entry.values()))

用法示例:

a=[{'a':'asdd','b':'asd'},{'a':'a','b':'asdsd'},{'a':1,'b':232323}]
print_list_of_dicts_as_table(a)

输出:

a    | b     
---- | ------
asdd | asd   
a    | asdsd 
   1 | 232323