The code below works (it will eventually pull records from a database via SQL), but I am having trouble understanding why the assert statement in the code below works.
def build_row(table, cols):
"""Build a class that creates instances of specific rows"""
class DataRow:
"""Generic data row class, specialized by surrounding function"""
def __init__(self, data):
"""Uses data and column names to inject attributes"""
assert len(data)==len(self.cols)
for colname, dat in zip(self.cols, data):
setattr(self, colname, dat)
def __repr__(self):
return "{0}_record({1})".format(self.table, ", ".join(["{0!r}".format(getattr(self, c)) for c in self.cols]))
DataRow.table = table
DataRow.cols = cols.split()
return DataRow
When is the length of ‘data’ determined and how is it guaranteed to be the same length as ‘self.cols’? Will someone please explain this behavior?
This may make more sense to you if you replace the references to
self.colsandself.tablewithself.__class__.colsandself.__class__.table. This confusion is caused by accessing class attributes through theselfobject as if they were instance attributes, and why I don’t particularly like this coding idiom. When looking at the code in__init__, where instance attributes are typically assigned their values, it is jarring to immediately see a statement that reads the value ofself.cols– where the heck didself.colsget initialized?! On the other hand, if you change this assert to:then it becomes clearer that the list of data values is expected to be the same length as the defined list of columns for this instance of DataRow. The class attribute
colsgets initialized right after the class definition, in this statement:long before any instance gets created – that’s why this assert works.
The writer of this code might consider converting to the more current
namedtupleconstruct.