5
\$\begingroup\$

I wrote this module in python for some minor tabulation needs in my science projects. This was very helpful for testing, debugging and plotting various data. I often use dictionaries for storing such data, either as an array or as in a spreadsheet (column and row). This tiny script helps me to plot such data in a similar fashion. Since I find myself using it a lot, I thought of improving it in terms of performance and readability. Also, I do understand that there are several third party libraries available for python to do the same with much more functionality, but I did it anyway for learning the language and ... well, and for fun. How could I refactor this code so that I could optimize it and possibly expand it in the future?

Summary

  • For dictionaries only
  • Rows can be specified separately for excluding some data
  • Automatic column spacing
  • Automatic table breaking (terminal size)
  • Specify headers and alignment

Dictionaries represent columns. Their keys marks the rows and their values contains the cell data. This produces the structure cell=column[row] which is very much similar to the A1 structure in spreadsheet programs.

I have added doc strings and comments to make this somewhat readable, but I would very much appreciate it if you could suggest an improvement. The Table class could be instantiated to specify rows and other features. User could then add columns using AddColumn method and that's pretty much it. Use Show to view the final table.

The [[]] in the code are used for containing info about each breaks in the table. [[break1], [break2], [break3] ...]. Each of these breaks contains columns that could fit inside the terminal window. [[c1,c2,c3...c7], [c8,c9], [c10,c11...] ...].

from shutil import get_terminal_size as termsize
class Table(object):
 """Lightweight tabulation class for dictionary type containers.
 Dictionaries represents columns of the table, and their values the cells.
 Keys identifies the rows of the table.
 """
 def __init__(self, rows, header="",align="<"):
 """Initialize the table by setting rows.
 'rows' must be an iterable containing the keys for every column. Each
 cell in the table is represented as : cell = column[row].
 Header for the row can be specified through 'head'.
 """
 # Row info
 self.rows = rows
 self.rowlength = self.GetMaxLength(rows, [header]) + 2
 self.rowstyle = "".join(("{:", align, str(self.rowlength), "}"))
 self.rowhead = self.rowstyle.format(str(header))
 # Table info (list of lists)
 self.columns = [[]]
 self.styles = [[]]
 self.headers = [[]]
 self.linelength = self.rowlength
 self.linelimit = termsize().columns
 def GetMaxLength(self, *iterables, threshold=0):
 """Returns the length of the longest item among the iterables.
 'threshold', if specified, must be the cut-off value of length.
 """
 for item in iterables:
 length = max(len(str(i)) for i in item)
 if length > threshold:
 threshold = length
 return threshold
 def AddColumn(self, content, header="", align="<"):
 """Adds a column to the table.
 'content' must be a dictionary containing the column values.
 Header for the column can be specified using 'header' argument.
 """
 length = self.GetMaxLength(content.values(), [header]) + 2
 style = "".join(("{:", align, str(length), "}"))
 self.linelength += length
 if self.linelength > self.linelimit:
 # Breaks the table
 self.columns.append([content])
 self.styles.append([style])
 self.headers.append([style.format(str(header))])
 self.linelength = self.rowlength + length
 else:
 # Resumes without breaking the table
 self.columns[-1].append(content)
 self.styles[-1].append(style)
 self.headers[-1].append(style.format(str(header)))
 def Show(self):
 """Displays the table."""
 for cols, stls, heds in zip(self.columns, self.styles, self.headers):
 # Print headers
 print("\n\x1b[7;1m", self.rowhead, "".join(heds), "\x1b[0m", sep="")
 # Print cells
 for row in self.rows:
 line = [s.format(str(c[row])) for s, c in zip(stls,cols)]
 print("\x1b[1m", self.rowstyle.format(row), "\x1b[0m",
 "".join(line), sep="")
def test():
 # Sample data
 from random import sample
 rows = sample(range(100,200), 10)
 data = {k:v for k,v in zip(rows, sample(Table.__doc__.split(),10))}
 # Tabulate them
 table = Table(rows, header="INDEX")
 for i in range(3):
 table.AddColumn(data, header="LEFT ALIGN")
 for i in range(3):
 table.AddColumn(data, header="CENTRE ALIGN", align="^")
 for i in range(3):
 table.AddColumn(data, header="RIGHT ALIGN", align=">")
 table.Show()
if __name__ == '__main__':
 test()
asked Sep 6, 2014 at 19:20
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

I think you shouldn't use the word threshold as a GetMaxLength parameter. It implies to me that it's a maximum value, but it's instead treated as a minimum. You then say it's the "cut-off value of length" which is ambiguous to me again. I'd just call it min and then it's self explanatory.

def GetMaxLength(self, *iterables, min=0):
 """Returns the length of the longest item among the iterables.
 'min', if specified, is the minimum possible value of length.
 """

Also I think draw is a more appropriate name for printing the table. You could possibly even implement it as __repr__ so that the table could be drawn with repr(Table). Though in that case I'd implement a nice __str__ method too in case people try to print it unwittingly in a sentence because '{}'.format(Table) would call __repr__ if you didn't have a __str__ method defined.

Your naming in general should match the style guide (PEP0008), which suggests that function names be all lowercase with underscores as separators, not CamelCase. So get_max_length would be more appropriate.

answered Aug 27, 2015 at 16:40
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.