Often times in the terminal, output from a program needs to be presented in tabular format. While many programming languages provide formatted printing, customizing the table printing to each use case could be tedious at times. In this article, a generic approach to printing tables on the terminal or command line using Python is discussed.
Environment
The code presented in this article is developed and executed using the tools listed below,
- Operating System - Windows 11
- IDE - JetBrains PyCharm
- Python version - 3.12.0
We will start by defining a class called Table, and build the needed methods within the table. Once the class is defined, multiple objects can be spawned using the same class, hence each object of the class could be viewed as a separate table on its own, and object may be modified and output independently.
The class contains the following fields,
- list of headers (these act as table headers, 1 for each column)
- list of rows (data rows) - number of columns
- length of the row (computed based on the number of columns)
The Table class, however, comes with a few restrictions to make the code simpler. These restrictions become part of the validations, and a corresponding exception is thrown in case of such violations. Some of the validations are,
- can not add columns to a non-empty table
- can not add rows which has number of columns more than the configured columns
- column alignment could only be either left or right
First, the class is defined and all the methods described later in this article go within the class.
class Table:
During the object initialization, the object specific fields in the table are given a default value.
def __init__(self): self.headers = [] self.rows = [] self.num_cols = 0 self.row_len = 0
The first method in the class is to add a column. The columns have to be configured in place before any data could be added inside the table. The method takes column name, maximum length of the column (i.e., column width) and an alignment option. The method validates the inputs, adds the header if inputs are valid and then updates the row length. The total number of columns is also kept track for the object.
def add_column(self, name, length, alignment="left"): self.validate(alignment) column = { "value": name, "length": length, "alignment": alignment.lower(), } self.headers.append(column) self.row_len += length self.num_cols += 1
The next method in the class is the validator for the column, - it prevents adding a column to a table which already has data (and), - validates the alignment input to be one of either "left" or "right"
def validate(self, alignment): if len(self.rows) != 0: raise ValueError("Can not add columns to table with existing data") if alignment.lower() not in ["left", "right"]: raise ValueError("Invalid value for alignment, expected only 'left' or 'right'")
The next method in the class is to add a given row, the input for the method is a list of data, equal to the number of columns configured. If the input data does not have all the columns or has additional columns, an exception will be raised.
def add_row(self, row): if len(row) != self.num_cols: raise ValueError("Length of input row does not match with number of columns") self.rows.append(row)
The next set of methods are for printing the table,
- print_line is used to print a horizontal line in the terminal using hyphens
- print_row is used to print the data for a given row, each row data is separated by a pipe ('|')
- print_header is used to print the header row
- print_table prints the header, rows in a nice format
To note, some of these print methods also take care of the alignment.
def print_line(self): computed_len = self.row_len + (1 + self.num_cols * 3) - 2 print(f"+{'-' * computed_len}+") def print_row(self, row): for i, element in enumerate(row): if self.headers[i]["alignment"] == "left": print(f"| {element:{self.headers[i]["length"]}} ", end="") else: print(f"| {element:>{self.headers[i]["length"]}} ", end="") print("|") def print_header(self): self.print_line() for element in self.headers: if element["alignment"] == "left": print(f"| {element["name"]:{element["length"]}} ", end="") else: print(f"| {element["name"]:>{element["length"]}} ", end="") print("|") self.print_line() def print_table(self): self.print_header() for row in self.rows: self.print_row(row) self.print_line()
Finally, some test code to see the Table in action.
table = Table() table.add_column("ID", 4, "right") table.add_column("Name", 16, "left") table.add_column("Salary", 8, "right") table.add_column("Commission", 10, "right") table.add_row([1001, "Gabriel", 9000, 700]) table.add_row([1002, "Elizabeth", 9500, 1250]) table.add_row([1003, "Wilson", 10500, 925]) table.add_row([1004, "Frankenstein", 8500, 1500]) table.add_row([1005, "Stephanie", 12000, 800]) table.print_table()
Running the test code will yield the below output,
+-------------------------------------------------+ | ID | Name | Salary | Commission | +-------------------------------------------------+ | 1001 | Gabriel | 9000 | 700 | | 1002 | Elizabeth | 9500 | 1250 | | 1003 | Wilson | 10500 | 925 | | 1004 | Frankenstein | 8500 | 1500 | | 1005 | Stephanie | 12000 | 800 | +-------------------------------------------------+
Thanks for reading. Please share any suggestions or errors through the comment section below.
Comments
Post a Comment