Добавление и извлечение таблиц при помощи модуля python-docx
При создании документа DOCX, часто встречается контент, который необходимо представить в виде аккуратной таблицы. Редактор MS Word неплохо справляется с этим. А вот как добавить таблицу при помощи модуля python-docx :
Пример добавления пустой таблицы, содержащей 2х2 ячейки:
from docx import Document # создание пустого документа doc = Document() # добавляем пустую таблицу 2х2 ячейки table = doc.add_table(rows=2, cols=2)
Объект таблицы Table имеет несколько свойств и методов, которые необходимо вызвать, чтобы заполнить таблицу данными. В качестве базового дальнейшего действия, всегда можно получить доступ к ячейке таблицы, исходя из ее расположения в строке и столбце:
cell = table.cell(0, 1)
Этот код возвратит объект ячейки Cell , которая расположена справа в верхней строке таблицы. Обратите внимание, что индексы строк и столбцов начинаются с нуля, как в списке.
В полученный объект ячейки можно записать какие-нибудь данные:
# добавляем данные как прогон абзаца # и выделяем текст жирным cell.paragraphs[0].add_run('Бык').bold = True # можно записать данные в ячейку проще cell.text = 'Бык' # что бы теперь отформатировать текст, нужно # получить доступ к свойствам прогона ячейки rc = cell.paragraphs[0].runs[0] rc.font.name = 'Arial' rc.font.bold = True
Часто бывает проще получить доступ к ряду ячеек одновременно, например, при заполнении таблицы переменной длины из источника данных. Свойство таблицы Table.rows предоставляет доступ к отдельным строкам, каждая из которых имеет свойство Table.rows[i].cells . Свойство .cells как в строке, так и в столбце поддерживает доступ к ячейке по индексу (как с списку):
# получаем 2-ю строку таблицы row = table.rows[1] # запись данных в ячейки row.cells[0].text = 'Заяц' row.cells[1].text = 'Волк'
Последовательности Table.rows и Table.columns в таблице являются итерируемыми, следовательно можно использовать их непосредственно в цикле for . То же самое с последовательностями ячеек, например для первой строки таблицы Table.rows[0].cells или для первого столбца Table.columns[0].cells :
# читаем ячейки таблицы `table` for row in table.rows: for cell in row.cells: print(cell.text)
Если необходимо узнать количество строк или столбцов в таблице, то просто используйте функцию len() для соответствующей последовательности:
# количество строк в таблице row_count = len(table.rows) # количество колонок в таблице col_count = len(table.columns)
Также можно добавлять строки в таблицу постепенно, например:
row = table.add_row()
Это может быть очень удобно для построения таблицы переменной длины:
# полноценный рабочий код from docx import Document from docx.enum.text import WD_ALIGN_PARAGRAPH # создание пустого документа doc = Document() # данные таблицы без названий колонок items = ( (7, '1024', 'Плюшевые котята'), (3, '2042', 'Меховые пчелы'), (1, '1288', 'Ошейники для пуделей'), ) # добавляем таблицу с одной строкой # для заполнения названий колонок table = doc.add_table(1, len(items[0])) # определяем стиль таблицы table.style = 'Light Shading Accent 1' # Получаем строку с колонками из добавленной таблицы head_cells = table.rows[0].cells # добавляем названия колонок for i, item in enumerate(['Кол-во', 'ID', 'Описание']): p = head_cells[i].paragraphs[0] # название колонки p.add_run(item).bold = True # выравниваем посередине p.alignment = WD_ALIGN_PARAGRAPH.CENTER # добавляем данные к существующей таблице for row in items: # добавляем строку с ячейками к объекту таблицы cells = table.add_row().cells for i, item in enumerate(row): # вставляем данные в ячейки cells[i].text = str(item) # если последняя ячейка if i == 2: # изменим шрифт cells[i].paragraphs[0].runs[0].font.name = 'Arial' doc.save('test.docx')
То же самое работает для столбцов, хотя строить таблицу таким способом не удобно.
MS Word имеет набор предварительно отформатированных стилей таблиц, которые можно выбрать из его галереи стилей таблиц. Применить один из них к таблице можно следующим образом:
table.style = 'Light Shading Accent 1'
Обратите внимание, что имя стиля таблицы немного отличается от имени, отображаемого в пользовательском интерфейсе MS Word. Дефис, если он есть, то его необходимо удалить. Например, Light Shading — Accent 1 становится Light Shading Accent 1 .
Чтобы узнать название стиля таблицы, наведите указатель мыши на его эскиз в галерее стилей таблиц Word.
Важно. Встроенные стили хранятся в файле WordprocessingML под своим английским именем, например ‘Table Grid’ , и не зависят от локализации MS Word. Так как модуль python-docx работает с файлом WordprocessingML , то поиск стиля должен использовать английское имя. Если файл WordprocessingML не найден (MS Word не установлен, например в OS Linux) то модуль python-docx работает со своей версией этого файла. Что бы создать сопоставление между именами стилей на русском языке и именами на английском языке посетите эту ссылку.
Все стили таблиц можно посмотреть, выполнив код:
>>> from docx import Document >>> from docx.enum.style import WD_STYLE_TYPE >>> doc = Document() >>> all_styles = doc.styles >>> table_styles = [s for s in all_styles if s.type == WD_STYLE_TYPE.TABLE] >>> for style in table_styles: . print(table_styles.name) # Normal Table # Table Grid # Light Shading # Light Shading Accent 1
Извлечение табличных данных их документов DOCX.
При чтении существующего документа DOCX, все находящиеся в нем объекты таблиц Table группируются в последовательности Document.tables . Следовательно, что бы узнать количество таблиц в документе, нужно вызвать функцию len() для этой последовательности.
Доступ к объектам таблиц будем осуществлять по индексу последовательности Document.tables .
# полноценный рабочий код from docx import Document doc = Document('test.docx') # последовательность всех таблиц документа all_tables = doc.tables print('Всего таблиц в документе:', len(all_tables)) # создаем пустой словарь под данные таблиц data_tables = i:None for i in range(len(all_tables))> # проходимся по таблицам for i, table in enumerate(all_tables): print('\nДанные таблицы №', i) # создаем список строк для таблицы `i` (пока пустые) data_tables[i] = [[] for _ in range(len(table.rows))] # проходимся по строкам таблицы `i` for j, row in enumerate(table.rows): # проходимся по ячейкам таблицы `i` и строки `j` for cell in row.cells: # добавляем значение ячейки в соответствующий # список, созданного словаря под данные таблиц data_tables[i][j].append(cell.text) # смотрим извлеченные данные # (по строкам) для таблицы `i` print(data_tables[i]) print('\n') print('Данные всех таблиц документа:') print(data_tables)
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Изменение макета документа, модуль python-docx
- Работа с текстом, модуль python-docx
- Работа с таблицей при помощи модуля python-docx
- Работа с объектом Style модуля docx-python
- Работа с рисунками, модуль python-docx
- Объект Document модуля python-docx
- Объект Section() модуля python-docx
- Объект Paragraph модуля python-docx
- Объект Run модуля python-docx
- Объект Table модуля python-docx
- Объект Font модуля python-docx
- Объект ParagraphFormat модуля python-docx, форматирование абзаца
- Объект длинны Length модуля python-docx
- Константы/перечисления модуля python-docx
- Создание документов DOCX из шаблонов jinja2
Ускоряем алгоритм по обработке таблиц в Python
Всем привет! Делюсь лайфхаками по обработке таблиц с помощью нюансов использования памяти Python.
11 показов
909 открытий
Часто в нашей работе приходится сталкиваться с обработкой данных для последующего исследования и анализа. Зачастую алгоритм обработки очень прост: найти вхождения подстроки в строку, проставить флаги в исходном дата фрейме в зависимости от условий, подсчитать количественные характеристики или метрики.
Однако часто происходит так, что при первом написании алгоритм работает долго, несмотря на простоту задачи. Сегодня хотелось бы поговорить о полезных приемах для ускорения обработки. Напишем несколько подходов к решению одной задачи и сравним время выполнения.
Рассмотрим такую задачу есть 2 таблицы: в одной находится информация с запросами, где в одном столбце находится идентификатор клиента, а в другом тело запроса; в другой таблице находятся список идентификаторов запросов, которые указываются в теле запроса из предыдущей таблицы.
Первая табличка выглядит так (отобрали интересующий столбец):
Пример содержания строки: «Запрос: 43ba-b25d -1ddbeae1-aa60-230b3aa3da8f от 1823-10-06. Нотариус: Иванов Иван Иванович», однако некоторые строки не типизированы и могут представлять из себя однострочный запрос: «H18B11zap04500057-007438459-00-071119.rtf»
Вторая табличка выглядит так (также интересующий столбец):
Задача была в том, чтобы найти вхождения строки из второй таблицы в первую. В первой таблице 556424 строк, во второй 496425 строк.
Казалось бы, задача легкая и понятная. Но так ли это? Давайте разбираться.
Для начала, как и в любой подобной обработке, необходимо импортировать нужные библиотеки, считать таблички и добавить колонку с флагами (по умолчанию равную 0).
import pandas as pd from tqdm import tqdm import numpy as np #загрузка таблиц aim_df = pd.read_excel(‘Таблица 1.xlsx’) search_df = pd.read_excel(‘Таблица 2.xlsx’) #добавление столбца с флагом (по умолчанию у всех 0) aim_df[‘flag’] = 0
Теперь переходим к самому интересному – написанию алгоритма поиска. Первое что приходит на ум — написать «в лоб» алгоритм для поиска подстроки в строке, и затем, там, где совпадение было, выставить флаг 1.
find_key = search_df[‘GUID’] for row in tqdm(range(1, aim_df.shape[0])): for key in find_key: if str(key) in aim_df.iloc[row][‘ memnto_mori ‘]: aim_df.loc[row, ‘flag’] = 1
Вариант работает очень долго, приблизительно 12988 часов или 541 день.
Рассуждая можно прийти к выводу, что в нашей табличке скорее всего есть дубли, что увеличивает время перебора. Их лучше удалить.
search_list = list(set([a.strip() for a in search_df[‘GUID’]]))
Также в наших таблицах очень много информации, которая для обработки нам не нужна и для облегчения вычислений можно выделить интересующий нас столбец из первой таблицы
mem = list(set(aim_df[‘ memnto_mori ‘]))
также в нашей получившейся переменной все еще много лишнего, из нее нам нужен только номер запроса, давайте и выделим его:
mem_filter = <> for i in range(len(mem)): if mem[i].find(‘Запрос: ‘) != -1: mem_filter[mem[i][mem[i].find(‘: ‘) + 2: mem[i].find(‘ от ‘)]] = mem[i] else: mem_filter[mem[i]] = mem[i]
Получаем на выходе словарик, где ключ – очищенный номер, а значение – исходная строка. Теперь мы можем непосредственно сравнить словарик с исходным списком из второй таблицы:
Сравниваем два листа, ищем в целевом столбце вхождения target, при нахождении добавляем в словарь и ставим 1
keys = mem_filter.keys() mem_find = <> for s in search_list: if s in keys: mem_find[s] = 1
Затем сливаем наши полученные результаты таким образом, чтобы на выходе получить словарь, где ключ – исходная строка запроса, в которой нашлось вхождение, а значение – 1.
Далее в результат записываем значение словаря фильтр (исходную строку) и присваиваем 1.
result = <> for m in mem_find.keys(): result[mem_filter[m]] = mem_find[m]
Дальше логично просто пройтись по всей первой табличке и если значение есть в result – ставим 1, если нет – 0
for row in tqdm(range(1, aim_df.shape[0])): if aim_df.iloc[row][ ‘ memnto_mori ‘] in result: aim_df.loc[row, ‘flag’] = 1
Вроде бы все хорошо, однако время работы все еще достаточно большое – около 1,5 часов.
Однако есть одна очень полезная функция, для который мы и делали именно словарик, а не просто список – map(), которая возвращает колонку с новыми значениями (в нашем случае – flag).
aim_df[‘flag’] = aim_df[‘ memnto_mori ‘].map(result) # В оставшиеся места, вместо Nan вставляем 0 aim_df[‘flag’].fillna(0, inplace = True) # Для красоты делаем колонку int aim_df.astype(<'flag': 'int64'>)'flag':>
На весь код таким решением:
Выполняется данный код всего за несколько секунд. Эта функция применяется к колонке датафрейма по которому делаем фильтр, аргументом функции является словарь, где ключ – значение, которое встречается в колонке, к которой применяется функция, а значение – значение, которое будет возвращаться в новую колонку.
Код, который представлен выше выполняется быстро и понятен, однако содержит много строчек и «лишних» переменных. Есть более лаконичное и короткое решение. Нам понадобится опять словарь исходных – очищенных строк, но только теперь ключом будет строка, а значением – очищенная строка. Совсем необязательно создавать словарь, можно реализовать через лямбда функцию, только необходимо применить их с большей осторожностью, так как они являются не просто читаемыми для других. Но давайте воспользуемся лямбда функцией, а результат запишем сразу в новый столбец в первой таблице, в нем будут содержаться очищенные значения:
aim_df[‘GUID_from_string’] = aim_df[‘memnto_mori’].map(lambda x: x.split(‘ ‘)[1] if x.find(‘Запрос: ‘) != -1 else x)
Затем вместо построчных сравнений вхождения подстроки и создания дополнительного словаря можно просто применить функцию merge:
res_df = pd.merge(aim_df, search_df[‘GUID’], how=’left’, left_on=’GUID_from_string’, right_on=’GUID’)
На выходе получаем res_df:
То есть напротив значений, которые нашлись во второй таблице — будут значения, а где не нашлись – Nan. Время выполнения такого кода занимает несколько секунд, а размер составляет всего 2 строки.
Pandas.merge() – базовая функция для всех слияний или объединений таблиц. Также помимо этой функции существует функция join(). Merge можно использовать двумя способами: df1.merge(right = df2) или pandas.merge(left=df1, right=df2), а join – df1.join(df2,…).
Основные различия между merge и join заключаются в следующих пунктах:
- Поиск по правой таблице — df1.join(df2) всегда осуществляется на основании индекса. В то время как df1.merge(df2) позволяет присоединить по одному или нескольким столбцам df2 или по индексу.
- Поиск по левой таблице — по умолчанию df1.join(df2) использует индекс df1.
- df1.join(df2) по умолчанию делает левое соединение в то время как merge создает внутреннее соединение (inner join) по умолчанию, этот параметр можно настроить у обеих функций.
В целом эти 2 функции взаимозаменяемы, по времени исполнения они не отличаются, главное помнить о небольших различиях и использовать их правильно.
Таким образом, зная некоторые нюансы использования памяти Python, что словари на поиск работают всегда в разы быстрее, чем поиск по списку, а также нескольких полезных функций, время выполнения нашего код сократилось более чем в 500 раз.
Некоторые шаги можно объединить для уменьшения количества кода. В данной публикации рассмотрено несколько вариантов решения для полного понимания процесса и наглядности.
P.S если в столбце к которому применяем функцию map() будут значения, которых не будет в словаре, то в новом столбце будет Nan. Их легко заменить на нужные значения функцией fillna.
Как ввести таблицу в питоне
Для отображения данных в виде таблицы параметру show предпочтительно передать значение «headings» (если надо отображать заголовки), либо » » (для таблицы без заголовков). Определим небольшую таблицу с тремя столбцами:
from tkinter import * from tkinter import ttk root = Tk() root.title("METANIT.COM") root.geometry("250x200") # определяем данные для отображения people = [("Tom", 38, "tom@email.com"), ("Bob", 42, "bob@email.com"), ("Sam", 28, "sam@email.com")] # определяем столбцы columns = ("name", "age", "email") tree = ttk.Treeview(columns=columns, show="headings") tree.pack(fill=BOTH, expand=1) # определяем заголовки tree.heading("name", text="Имя") tree.heading("age", text="Возраст") tree.heading("email", text="Email") # добавляем данные for person in people: tree.insert("", END, values=person) root.mainloop()
Здесь данные, которые будут отображаться в таблице, определены в виде списка people, который хранит набор кортежей. Каждый кортеж состоит из трех элементов. Условно будем считать, что первый элемент кортежа представляет имя пользователя, второй — возраст, а третий — электронный адрес. И эти данные нам надо отобразить в таблице:
people = [("Tom", 38, "tom@email.com"), ("Bob", 42, "bob@email.com"), ("Sam", 28, "sam@email.com")]
Для отображения этих данных определяем три столбца: name, age и email в виде кортежа и передаем их параметру columns :
columns = ("name", "age", "email") tree = ttk.Treeview(columns=columns, show="headings")
Далее нам надо настроить заголовки столбца с помощью метода heading() класса Treeview (по умолчанию столбцы не имеют никаких заголовков). Данный метод принимает ряд параметров:
tree.heading("name", text="Имя")
Первый параметр указывает на имя столбца. В примере выше определяем также параметр text , который определяет текст заголовка
И последний момент — добавляем сами данные в таблицу с помощью метода insert() класса Treeview
tree.insert("", END, values=person)
Первый параметр — пустая строка «» указывает, что элемент добавляется как элемент верхнего уровня (то есть у него нет родительского элемента). Значение END указывает, что элемент добавляется в конец набора. И параметр values в качестве добавляемых данных устанавливает кортеж person.
В итоге мы получим следующую таблицу:
Настройка столбца
Вполне возможно, что изначальные настройки столбцов нас не устроят. Например, текст заголовка располагается по умолчанию по центру, а данные столбца выравниваются по левому краю. Кроме того, каждый столбец имеет некоторую начальную ширину, в следствие чего ширина виджета может оказаться больше ширины окна. Либо мы захотим как-то иначе настроить вид столбца.
Прежде всего мы можем настроить заголовки столбца с помощью метода heading() :
heading(column, text, image, anchor, command)
- column : имя настраиваемого столбца
- text : текст заголовка
- image : картинка для заголовка
- anchor : устанавливает выравнивание заголовка по определенному краю. Может принимать значения n, e, s, w, ne, nw, se, sw, c
- command : функция, выполняемая при нажатии на заголовок
Для настройки столбца в целом применяется метод column() :
column(column, width, minwidth, stretch, anchor)
- column : индекс настраиваемого столбца в формате «# номер_столбца»
- width : ширина столбца
- minwidth : минимальная ширина
- anchor : устанавливает выравнивание заголовка по определенному краю. Может принимать значения n, e, s, w, ne, nw, se, sw, c
- stretch : указывает, будет ли столбец растягиваться при растяжении контейнера. Если будет, то значение True , иначе значение False
Применим некоторые из этих параметров:
from tkinter import * from tkinter import ttk root = Tk() root.title("METANIT.COM") root.geometry("250x200") # определяем данные для отображения people = [("Tom", 38, "tom@email.com"), ("Bob", 42, "bob@email.com"), ("Sam", 28, "sam@email.com")] # определяем столбцы columns = ("name", "age", "email") tree = ttk.Treeview(columns=columns, show="headings") tree.pack(fill=BOTH, expand=1) # определяем заголовки с выпавниваем по левому краю tree.heading("name", text="Имя", anchor=W) tree.heading("age", text="Возраст", anchor=W) tree.heading("email", text="Email", anchor=W) # настраиваем столбцы tree.column("#1", stretch=NO, width=70) tree.column("#2", stretch=NO, width=60) tree.column("#3", stretch=NO, width=100) # добавляем данные for person in people: tree.insert("", END, values=person) root.mainloop()
В данном случае для заголовков устанавливаем выравнивание по левому краю. Для столбцов запрещаем растяжение и устанавливаем ширину.

При добавлении изображения оно помещается в правой части. Например, установка изображения для третьего столбца:
# предполагается, что в папке приложения располагается файл email_icon_micro.png email_icon = PhotoImage(file="./email_icon_micro.png") tree.heading("email", text="Email", anchor=W, image=email_icon)

Добавление к Treeview прокрутки
from tkinter import * from tkinter import ttk root = Tk() root.title("METANIT.COM") root.geometry("250x200") root.rowconfigure(index=0, weight=1) root.columnconfigure(index=0, weight=1) # определяем данные для отображения people = [ ("Tom", 38, "tom@email.com"), ("Bob", 42, "bob@email.com"), ("Sam", 28, "sam@email.com"), ("Alice", 33, "alice@email.com"), ("Kate", 21, "kate@email.com"), ("Ann", 24, "ann@email.com"), ("Mike", 34, "mike@email.com"), ("Alex", 52, "alex@email.com"), ("Jess", 28, "jess@email.com"), ] # определяем столбцы columns = ("name", "age", "email") tree = ttk.Treeview(columns=columns, show="headings") tree.grid(row=0, column=0, sticky="nsew") # определяем заголовки tree.heading("name", text="Имя", anchor=W) tree.heading("age", text="Возраст", anchor=W) tree.heading("email", text="Email", anchor=W) tree.column("#1", stretch=NO, width=70) tree.column("#2", stretch=NO, width=60) tree.column("#3", stretch=NO, width=100) # добавляем данные for person in people: tree.insert("", END, values=person) # добавляем вертикальную прокрутку scrollbar = ttk.Scrollbar(orient=VERTICAL, command=tree.yview) tree.configure(yscroll=scrollbar.set) scrollbar.grid(row=0, column=1, sticky="ns") root.mainloop()
Как напечатать таблицу с помощью f-string
Разберём, как напечатать красивую таблицу с одинаковой шириной колонок, с разной шириной колонок, с шапкой из двух строк. Создадим функцию с параметром максимальной ширины таблицы и функцию для записи таблицы в текстовый файл.
В этой статье мы разберём как напечатать красивые таблицы:
- с одинаковой шириной колонок;
- с разной шириной колонок;
- с шапкой из двух строк.
А также создадим функции:
- с параметром максимальной ширины таблицы;
- для записи таблицы в текстовый файл.
«F-строки» были введены ещё в версии Python 3.6, и все уже давно, наверно, их используют в своём коде. Для тех, кто хочет освежить память, и ещё раз перечитать документацию — PEP 498 – Literal String Interpolation. Мы же будем использовать «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))
Функция с параметром максимальной ширины таблицы
Давайте соберём наш код в функцию, но добавим ещё один параметр — максимальную ширину таблицы. И если ширина таблицы будет больше максимальной, заданной по умалчиванию — выведем сообщение. А также сделаем выравнивание текста в строках шапки таблицы по центру, в теле таблицы — по правому краю. Для этого надо всего лишь перед значением ширины строки вставить символ «^» — выравнивание по центру, «>» — выравнивание по правому краю, «
Давайте ещё изменим вывод нашей таблицы. Колонки «Цена $» и «Пробег км» выведем с , , как разделитель тысяч.
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) >', end='') # выравниване по ценру else: print('Ширина таблицы больше допустимого значения') return print() # печать разделителя шапки '=' print(f"") # печать тела таблицы for el in data: for n, col in enumerate(el): if n < 3: print(f'>', end='') # выравнвание по правому краю else: print(f'', end='') # выравнвание по правому краю наследуется, с разделителем тысяч «,» print()
Здесь, в 52 строке кода, в f’,> ‘ перед закрывающей фигурной скобкой вставлена запятая, как разделитель тысяч (кроме «,» можно ещё использовать «_»), ну а дальше форматирование сделает всё само:
Но это ещё не все возможности «спецификации формата».
Примечание: в 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) >') else: print('Ширина таблицы больше допустимого значения') return f.write('\n')
