Как распечатать список диктов в виде выровненной таблицы? - PullRequest
0 голосов
/ 26 октября 2018

Итак, после прохождения нескольких вопросов, касающихся выравнивания с использованием спецификаторов формата, я все еще не могу понять, почему числовые данные печатаются на стандартный вывод волнообразным способом.

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-строкисо спецификаторами форматирования, но все равно он печатает данные следующим образом, может кто-нибудь, пожалуйста, помогите мне разобраться в этом.

 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, которая отлично работает.Но я также хочу научиться делать это без какой-либо библиотеки.

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

Как уже упоминалось, вы неправильно рассчитали длины строк.
Вместо того, чтобы жестко их кодировать, делегируйте эту задачу вашей программе.

Вот общий подход:

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

Подробнее см. документы . Также есть примеры с выравниванием.

0 голосов
/ 26 октября 2018

Вы получили неверный результат, потому что вы не посчитали правильную длину заголовков.Вы зарезервировали только 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

(Только дополнительное)

Вот более обобщенный подход, который использует список имен ключей для печати и способен генерировать все остальные необходимые переменные на лету.Не нужно жестко кодировать имена переменных и фиксировать их порядок - порядок берется из этого списка.Корректировки отображаемых элементов идут в одном месте: тот же список, 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. The *Список 1020 * заполняется максимальной длиной каждого именованного ключа, взятого из списка get_items.
  2. Возвращает list;fmtstring повторяет инструкцию формата {:d} для каждого из этих пунктов и заполняет номер.Внешние {{: и }} переводятся format в {: и }, поэтому конечный результат будет {:<em>number</em>} для каждой длины.Эти отдельные строки формата объединяются в одну более длинную строку формата.
  3. Наконец, цикл над реальными данными печатает элементы из get_items.Понимание списка ищет их;запись * заставляет список «записываться» как отдельные значения, а не возвращать весь список как единое целое.

Благодаря @ Georgy дляпредлагая поискать менее жесткие сорта.

...