Как напечатать таблицу с помощью f-string

В этой статье мы разберём как напечатать красивые таблицы:
- с одинаковой шириной колонок;
- с разной шириной колонок;
- с шапкой из двух строк.
А также создадим функции:
- с параметром максимальной ширины таблицы;
- для записи таблицы в текстовый файл.
«F-строки» были введены ещё в версии Python 3.6, и все уже давно, наверно, их используют в своём коде. Мы же будем использовать «f-строки» для вывода данных в табличном виде. Для примера возьмём данные об автомобилях с одного из онлайн-рынков такой структуры:
data = [
['Volkswagen', 'Golf V', '2008', '8000', '154000'],
['Mazda', 'CX-5', '2013', '14800', '117000'],
['Honda', 'CR-V AWD', '2017', '22000', '57000'],
['BMW', '320', '2015', '14700', '124000'],
['BMW', 'X1', '2012', '17000', '62000'],
['Mercedes-Benz', 'E 220', '2009', '9300', '240000'],
['Volkswagen', 'Golf VI STYLE', '2011', '9700', '203000'],
['Mazda', '6', '2006', '5600', '218000'],
['Hyundai', 'Tucson LOUNGE 2009', '2008', '8899', '149000'],
['BMW', '520', '2013', '21700', '146000'],
['Toyota', 'Highlander', '2015', '28000', '120000'],
['Mercedes-Benz', 'E 220', '2005', '8200', '276000'],
['BMW', '328', '2012', '12500', '260000'],
['Opel', 'Astra J', '2013', '9500', '224000'],
['Volkswagen', 'Passat B7', '2013', '11750', '138000'],
['Audi', 'A6 Quattro', '2006', '8000', '28000']
]
Названия колонок:
columns = ['Марка', 'Модель', 'Год', 'Цена', 'Пробег']
Таблица с одинаковой шириной колонок
Для того, чтоб данные в колонках таблицы выровнять по центру, левому или правому краю, нужно рассчитать ширину колонок и определить отступ. Давайте сделаем ширину колонок одинаковой по максимальной строке столбца и отступом в один символ.
# расчёт максимальной длинны колонок
max_columns = [] # список максимальной длинны колонок
for col in zip(*data):
len_el = []
[len_el.append(len(el)) for el in col]
max_columns.append(max(len_el))
Печать таблицы:
# печать таблицы с колонками максимальной длинны строки
# печать шапки таблицы
for column in columns:
print(f'{column:{max(max_columns)+1}}', end='')
print()
# печать разделителя шапки
print(f'{"="*max(max_columns)*5}')
# печать тела таблицы
for el in data:
for col in el:
print(f'{col:{max(max_columns)+1}}', end='')
print()
Здесь в 4 строке кода, после : указана ширина колонок. Так как это не число, а выражение, его обрамляют фигурными скобками. Ширину колонок мы сделали по максимальной длине строки колонок и добавили еще один символ пустой строки '' для отступов, end='' – указывает, что печать будет выводиться без переноса на новую строку. Выравнивание текста в строке по умалчиванию делается по левому краю, поэтому его не указываем. Далее будут рассмотрены все виды выравнивания.
Результат:

Недостатки такой таблицы:
- большая ширина (подходит для таблиц с примерно одинаковой длинной строк колонок);
- трудно вычислить длину разделителя шапки.
Таблица с разной шириной колонок
Рассмотрим другой пример. Выведем таблицу с колонками максимальной длины строки каждого столбца.
# вывод таблицы с колонками максимальной длинны строки каждого столбца
# печать шапки таблицы
for n, column in enumerate(columns):
print(f'{column:{max_columns[n]+1}}', end='')
print()
# печать разделителя шапки '='
r = f'{"="*sum(max_columns)+"="*5}'
print(r[:-1])
# печать тела таблицы
for el in data:
for n, col in enumerate(el):
print(f'{col:{max_columns[n]+1}}', end='') # выравнвание по правому краю >
print()
Результат:

Теперь таблица стала компактнее.
В этом примере названия колонок меньше или равны ширине самих колонок. А что, если они будут больше? Например, состоять из двух слов.
Таблица с шапкой из двух строк
Если ширина названия колонок больше ширины колонок тела таблицы, тогда запишем их в шапке таблицы в две строчки — вот так:

Но теперь колонки названий могут быть шире рассчитанных ранее колонок тела таблицы. Придётся отдельно просчитать максимальные значения ширины колонок шапки и максимальные значения колонок тела таблицы, сравнить их и по большему значению задать ширину столбиков для всей таблицы.
# пишем название колонок в две строчки
columns = [['Марка', 'Модель', 'Год', 'Цена $', 'Пробег км'],
['автомобиля', '', 'выпуска', '', '']]
# вычислить максимальную длинну колонки шапки таблицы
max_columns_title = [] # список максимальной ширины колонок шапки
for col in zip(*columns):
max_columns_title.append(max([len(el) for el in col]))
max_col_title = max(max_columns_title) # максимальная ширина колонки шапки
for col in columns:
#width = []
for n, c in enumerate(col):
# сравниваем максимальную колонку шапки с макс колонкой таблицы
if max_columns[n] >= max_columns_title[n]:
w = max_columns[n] + 2
width.append(w)
else:
w = max_columns_title[n] + 2
width.append(w)
# пишем название колонок в две строчки
print(f'{c:{w}}', end='')
print()
# печать разделителя шапки '='
print(f"{'='*(sum(width)-2)}")
# печать тела таблицы
for el in data:
for n, col in enumerate(el):
print(f'{col:{width[n]}}', end='')
print()
print()
Результат:

Функция с параметром максимальной ширины таблицы
Давайте соберём наш код в функцию, но добавим ещё один параметр — максимальную ширину таблицы. И если ширина таблицы будет больше максимальной, заданной по умалчиванию — выведем сообщение. А также сделаем выравнивание текста в строках шапки таблицы по центру, в теле таблицы — по правому краю. Для этого надо всего лишь перед значением ширины строки вставить символ «^» — выравнивание по центру, «>» — выравнивание по правому краю, «<» – выравнивание по левому краю, выставлено по умолчанию.
def print_table(data, columns, indent, max_width=100):
# data — список списков, данные таблицы
# columns — список списков, названия колонок таблицы
# indent — отступ от края колонки
# max_widt – допустимая ширина таблицы
# max_columns — список максимальной длинны строки колонок
# max_columns_title — список максимальной ширины колонок шапки
# width — список ширины каждой колонки таблицы для печати
# расчёт макимальной ширины колонок таблицы
max_columns = []
for col in zip(*data):
len_el = []
[len_el.append(len(el)) for el in col]
max_columns.append(max(len_el))
# вычислить максимальную длинну колонки шапки таблицы
max_columns_title = []
for col in zip(*columns):
max_columns_title.append(max([len(el) for el in col]))
# печать таблицы
for col in columns:
width = []
for n, c in enumerate(col):
# сравниваем максимальную колонку шапки с макс колонкой таблицы
if max_columns[n] >= max_columns_title[n]:
w = max_columns[n] + indent
width.append(w)
else:
w = max_columns_title[n] + indent
width.append(w)
# пишем название колонок в две строки
if sum(width) <= max_width:
print(f'{c:^{w}}', end='') # выравниване по ценру
else:
print('Ширина таблицы больше допустимого значения')
return
print()
# печать разделителя шапки '='
print(f"{'='*(sum(width))}")
# печать тела таблицы
for el in data:
for n, col in enumerate(el):
print(f'{col:>{width[n]}}', end='') # выравнвание по правому краю
print()
print_table(data, columns, 1, max_width=100)
Результат:

Давайте ещё изменим вывод нашей таблицы. Колонки «Цена $» и «Пробег км» выведем с ,, как разделитель тысяч.
def print_table(data, columns, indent, max_width=100):
# data — список списков, данные таблицы
# columns — список списков, названия колонок таблицы
# indent — отступ от края колонки
# max_widt — допустимая ширина таблицы
# max_columns — список максимальной длинны строки колонок
# max_columns_title — список максимальной ширины колонок шапки
# width — список ширины каждой колонки таблицы для печати
# расчёт макимальной ширины колонок таблицы
max_columns = []
for col in zip(*data):
len_el = []
[len_el.append(len(el)) for el in col]
max_columns.append(max(len_el))
# вычислить максимальную длинну колонки шапки таблицы
max_columns_title = []
for col in zip(*columns):
max_columns_title.append(max([len(el) for el in col]))
# печать таблицы
for col in columns:
width = []
for n, c in enumerate(col):
# сравниваем максимальную колонку шапки с макс колонкой таблицы
if max_columns[n] >= max_columns_title[n]:
w = max_columns[n] + indent
width.append(w)
else:
w = max_columns_title[n] + indent
width.append(w)
# пишем название колонок в две строки
if sum(width) <= max_width:
print(f'{c:^{w}}', end='') # выравниване по ценру
else:
print('Ширина таблицы больше допустимого значения')
return
print()
# печать разделителя шапки '='
print(f"{'='*(sum(width))}")
# печать тела таблицы
for el in data:
for n, col in enumerate(el):
if n < 3:
print(f'{col:>{width[n]}}', end='') # выравнвание по правому краю
else:
print(f'{int(col):{width[n]},}', end='') # выравнвание по правому краю наследуется, с разделителем тысяч «,»
print()
Здесь, в 52 строке кода, в f'{int(col):{width[n]},}‘ перед закрывающей фигурной скобкой вставлена запятая, как разделитель тысяч (кроме «,» можно ещё использовать «_»), ну а дальше форматирование сделает всё само:

Но это ещё не все возможности «спецификации формата».
Примечание: в F-string в фигурных скобках{}помещены «заменяющие поля». Двоеточие:указывает на полеformat_spec, что означает нестандартный формат замены. Для него существует Мини-язык спецификации формата.
Функция для записи таблицы в текстовый файл
Часто приходится не только печатать таблицу, но и сохранять её в текстовом файле. Имея готовую функцию для печати, нетрудно переделать её для записи.
def write_table(file_name, data, columns, indent, max_width=100):
# file_name — название текстового файла
# data — список списков, данные таблицы
# columns — список списков, названия колонок таблицы
# indent — отступ от края колонки
# max_widt — допустимая ширина таблицы
# max_columns — список максимальной длинны строки колонок
# max_columns_title — список максимальной ширины колонок шапки
# width — список ширины каждой колонки таблицы для печати
# расчёт макимальной ширины колонок таблицы
max_columns = []
for col in zip(*data):
len_el = []
[len_el.append(len(el)) for el in col]
max_columns.append(max(len_el))
# вычислить максимальную длинну колонки шапки таблицы
max_columns_title = []
for col in zip(*columns):
max_columns_title.append(max([len(el) for el in col]))
# запись таблицы
with open(file_name, 'w', encoding='utf-8') as f:
for col in columns:
width = []
for n, c in enumerate(col):
# сравниваем максимальную колонку шапки с макс колонкой таблицы
if max_columns[n] >= max_columns_title[n]:
w = max_columns[n] + indent
width.append(w)
else:
w = max_columns_title[n] + indent
width.append(w)
# пишем название колонок в две строки
if sum(width) <= max_width:
f.write(f'{c:^{w}}')
else:
print('Ширина таблицы больше допустимого значения')
return
f.write('\n')